Warning
Use at your own risk. Cal.flare is the open source community edition of Cal.com and it is intended for users who want to self-host their own Cal.flare instance. It is strictly recommended for personal, non-production use. Please review all installation and configuration steps carefully. Self-hosting requires advanced knowledge of Cloudflare administration, D1 database management, and securing sensitive data. Proceed only if you are comfortable with these responsibilities.
Tip
For any commercial and enterprise-ready scheduling infrastructure, use Cal.com, not Cal.flare; hosted by us or get invited to on-prem enterprise access here: https://cal.com/sales
The community-driven, open-source scheduling platform.
GitHub
Issues
·
Contributing
About Cal.flare
Cal.flare ports the community-driven, fully open-source Cal.diy scheduling platform to Cloudflare Workers and D1. Cal.diy is based on Cal.com, with its enterprise/commercial code removed.
Cal.flare is 100% MIT-licensed with no proprietary "Enterprise Edition" features. It's designed for individuals and self-hosters who want full control over their scheduling infrastructure without any commercial dependencies.
What's different from Cal.com?
- No enterprise features — Teams, Organizations, Insights, Workflows, SSO/SAML, and other EE-only features have been removed
- No license key required — Everything works out of the box, no Cal.com account or license needed
- 100% open source — The entire codebase is licensed under MIT, no "Open Core" split
- Community-maintained — Contributions are welcome and go directly into this project (see CONTRIBUTING.md)
Note: Cal.flare is a self-hosted project. There is no hosted/managed version. You run it on your own infrastructure.
Built With
The port plan, architecture and inventory describe the Cloudflare/Bun substitutions. Prisma remains the normal data-access layer through the official D1 adapter. Existing atomic multi-row operations use native guarded D1 batches; see D1 and Prisma usage.
Getting Started
To get a local copy up and running, please follow these simple steps.
Prerequisites
Here’s what you need to run Cal.flare.
- Bun 1.4.4
- Node.js 24.22.0 for build tools that require it
- A Cloudflare Workers Paid plan to deploy Cal.flare
- Local workerd/D1 for development
If you want to enable any of the available integrations, you may want to obtain additional credentials for each one. More details on this can be found below under the integrations section.
Development
Setup
-
Clone the repo (or fork https://github.com/ralphbibera/cal.flare/fork)
git clone git@github.com:ralphbibera/cal.flare.git
If you are on Windows, run the following command in Git Bash with admin privileges:
git clone -c core.symlinks=true git@github.com:ralphbibera/cal.flare.git -
Go to the project folder
-
Select Bun 1.4.4 and Node 24.22.0 for build tooling. If you use nvm:
nvm install 24.22.0 && nvm use 24.22.0 bun --version -
Install packages with Bun:
bun install --frozen-lockfile
-
Set up local Worker configuration:
- Set consumed variables in an ignored
.dev.varsfile for the web app. Use .env.example and .env.appStore.example for existing names, with the Cloudflare binding and secret configuration. - Generate
NEXTAUTH_SECRETwithopenssl rand -base64 32. - Generate
CALENDSO_ENCRYPTION_KEYwithopenssl rand -base64 24. - Store each value in the consuming Worker's
.dev.varsfile for local development, and in Worker secrets for deployment.
- Set consumed variables in an ignored
-
Bind the web Worker to the local D1 database and apply the converted migrations. See the D1 configuration reference.
-
Create your first user, or load the development fixtures below.
-
Start the application:
Development test users
Development seed credentials:
| Password | Role | |
|---|---|---|
free@example.com |
free |
Free user |
pro@example.com |
pro |
Pro user |
trial@example.com |
trial |
Trial user |
admin@example.com |
ADMINadmin2022! |
Admin user |
onboarding@example.com |
onboarding |
Onboarding incomplete |
After running the synthetic development seed, you can use these credentials at your local application origin. Keep these fixture accounts out of production data.
Tip: Inspect the seeded records using the local D1 tools. PostgreSQL Prisma Studio is not the D1 inspection path.
Development tip
-
Add
export NODE_OPTIONS="--max-old-space-size=16384"to your shell script to increase the memory limit for a Node build-tool process. This does not change Worker runtime limits. Alternatively, you can run this in your terminal before running the app. Replace 16384 with the amount of RAM you want to allocate to the node process. -
Add
NEXT_PUBLIC_LOGGER_LEVEL={level}to local Worker configuration to control the logging verbosity for all tRPC queries and mutations.
Where {level} can be one of the following:0for silly
1for trace
2for debug
3for info
4for warn
5for error
6for fatalWhen you set
NEXT_PUBLIC_LOGGER_LEVEL={level}in local Worker configuration, it enables logging at that level and higher. Here's how it works:The logger will include all logs that are at the specified level or higher. For example: \
- If you set
NEXT_PUBLIC_LOGGER_LEVEL=2, it will log from level 2 (debug) upwards, meaning levels 2 (debug), 3 (info), 4 (warn), 5 (error), and 6 (fatal) will be logged. \ - If you set
NEXT_PUBLIC_LOGGER_LEVEL=3, it will log from level 3 (info) upwards, meaning levels 3 (info), 4 (warn), 5 (error), and 6 (fatal) will be logged, but level 2 (debug) and level 1 (trace) will be ignored. \
- If you set
echo 'NEXT_PUBLIC_LOGGER_LEVEL=3' >> apps/web/.dev.vars
for Logger level to be set at info, for example.
Gitpod Setup
Use the same Bun, Node build-tool and local workerd/D1 setup in a Gitpod workspace. Forward the development port and configure application/auth URLs for that origin. The inherited Docker/PostgreSQL workspace tasks need the Cloudflare substitutions before they can be used for this target.
Setting up your first user
Approach 1
Open the application after its D1 schema and authentication configuration are ready. Use the setup/signup flow to create your first user. This runs the existing password hashing, profile defaults, verification and admin policy instead of bypassing them with manual row edits.
Approach 2
Run the ported synthetic seed against local D1. It applies pending D1 migrations first, writes to the web Worker's local state, and adds no rows when run again. The upstream seed defines the dummy users above.
bun run --cwd packages/prisma db-seed:d1 --local-only
Do not use development seed accounts for a production installation.
E2E-Testing
Point the retained Playwright runner at the vinext/workerd application and D1. Keep its existing assertions and scenarios.
Be sure to set the environment variable NEXTAUTH_URL to the correct value. If you are running locally, as the documentation within .env.example mentions, the value should be http://localhost:3000.
# In a terminal just run: bun run test-e2e # To open the last HTML report run: bunx playwright show-report test-results/reports/playwright-html-report
The suite enables MailHog for the email journeys by default. Start the development mail receiver below before running the tests.
Development mail receiver
Run mailhog to view emails sent during development
NOTE: Required when
E2E_TEST_MAILHOG_ENABLEDis "1"
docker pull mailhog/mailhog docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog
Resolving issues
E2E test browsers not installed
Run bunx playwright install to download test browsers and resolve the error below when running bun run test-e2e:
Executable doesn't exist at /Users/alice/Library/Caches/ms-playwright/chromium-1048/chrome-mac/Chromium.app/Contents/MacOS/Chromium
Upgrading from earlier versions
-
Pull the current version:
-
Check if dependencies got added/updated/removed
bun install --frozen-lockfile
-
Apply the reviewed D1 migrations in order. Back up production data and retain a recovery procedure before changing its schema. Use the local D1 database for development; do not run a development reset or seed against production.
-
Check for Worker variable, secret and binding changes
-
Start the server. In a development environment, just do:
For a production build, run for example:
bun run build bun run --cwd apps/web deploy
-
Enjoy the new version.
Deployment
Cloudflare Workers
Use the native vinext Cloudflare integration and each application's vite.config.ts and cloudflare.config.ts.
The Cloudflare setup guide covers local development, secrets, D1 and deployment configuration.
Requirements
- Cloudflare Workers Paid is required to deploy Cal.flare.
- The web Worker uses the authoritative D1 database.
ASSETSserves public files, fonts, embeds and built output.- The web Worker uses the selected cache bindings for cache resources.
- The upstream Trigger.dev tasker is optional. Tasks run inline unless
ENABLE_ASYNC_TASKER="true",TRIGGER_SECRET_KEYandTRIGGER_API_URLare all set. - The docs application has its own static assets and no application database binding.
Deploying Cal.flare
Follow the Cloudflare deployment procedure for D1 migration ordering, resource bindings, application builds and provider verification. Keep source configuration and ordered migrations in Git; generated build output and secrets stay outside it.
Configuration
Important Run-time variables
Provide these values through Worker bindings, variables and secrets:
| Variable or binding | Description | Required |
|---|---|---|
DB |
D1 application/auth database. | required |
NEXT_PUBLIC_WEBAPP_URL |
Public web origin, consistent with client build configuration. | required |
NEXTAUTH_URL |
Public authentication origin and callback configuration. | required |
NEXTAUTH_SECRET |
Installation session/signing secret; generate during Setup. | required |
CALENDSO_ENCRYPTION_KEY |
Stable 32-character Cal encryption key; generate during Setup. | required |
CRON_API_KEY |
Authentication for retained cron HTTP endpoints. | where consumed |
EMAIL_FROM |
Sender identity for application mail. | for mail delivery |
Mail uses RESEND_API_KEY, EMAIL_SERVER, or the existing SMTP host/port/user/password fields.
Use a supported public TLS/STARTTLS relay on Workers; local sendmail is not available there.
Store private values as Worker secrets. Use ignored .dev.vars files for local development, and configure each consuming Worker.
Build-time variables
Provide consumed public values through the explicit client-build allowlist:
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_WEBAPP_URL |
Base URL injected into client output. | local development origin |
NEXT_PUBLIC_WEBSITE_URL |
Website origin where consumed. | application configuration |
NEXT_PUBLIC_EMBED_LIB_URL |
Public embed script URL. | application configuration |
NEXT_PUBLIC_WEBSITE_TERMS_URL |
Optional terms and conditions URL. | |
NEXT_PUBLIC_WEBSITE_PRIVACY_POLICY_URL |
Optional privacy-policy URL. | |
CALCOM_TELEMETRY_DISABLED |
Set to 1 to disable telemetry where consumed. |
Rebuild client assets when a public build-time value changes. Auth, encryption and provider secrets remain server-side. See the environment inventory for retained names and the architecture for exact resource bindings.
Troubleshooting
SSL edge termination
Cloudflare handles public TLS. Keep the application/auth origins and provider callbacks consistent with the actual HTTPS domain. Fix certificate, trust or URL configuration instead of disabling TLS verification.
Failed to commit changes: Invalid 'prisma.user.create()'
Inspect the actual input and D1/Prisma error for the failing field or relation. Preserve the original metadata, null and generated-ID contract. Use the setup/signup service so password hashing and user defaults run; do not bypass those services with manual rows.
CLIENT_FETCH_ERROR
Check NEXTAUTH_URL, the public web origin, cookie domain/secure settings and installation secrets.
Inspect the Worker auth response and its Set-Cookie headers, then compare the original server/browser session projections.
Docker service-name and loopback-DNS workarounds are not the Worker routing model.
License
Cal.flare is fully open source, licensed under the MIT License.
Unlike Cal.com's "Open Core" model, Cal.flare has no commercial/enterprise code. The entire codebase is available under the same open-source license.
Enabling Content Security Policy
- Set CSP_POLICY="non-strict" env variable, which enables Strict CSP except for
unsafe-inlineinstyle-src. If you have custom changes in your instance, you may need to modify your code to make it CSP-compatible. Currently, strict CSP is enabled only on the login page. On other SSR pages, it is enabled in report-only mode to detect potential issues. It is not yet supported on SSG pages.
Integrations
Provider credentials and consent are still required on Cloudflare. Use your public web origin for callback URLs.
Store deployment secrets in Worker secret bindings and local values in ignored .dev.vars files. Apply a secret to each Worker that consumes it.
Some apps use Settings → Admin → Apps key fields instead of environment variables; keep that adapter's key schema.
The integration guide and complete catalog cover every app-store definition and existing per-app setup guide.
Obtaining the Google API Credentials
- Open Google API Console. If you don't have a project in your Google Cloud subscription, you'll need to create one before proceeding further. Under Dashboard pane, select Enable APIS and Services.
- In the search box, type calendar and select the Google Calendar API search result.
- Enable the selected API.
- Next, go to the OAuth consent screen from the side pane. Select the app type (Internal or External) and enter the basic app details on the first page.
- Add the Calendar scopes requested by the adapter:
https://www.googleapis.com/auth/calendar.eventsandhttps://www.googleapis.com/auth/calendar.readonly. Keep the profile/email scopes required by the enabled login flow. - In the third page (Test Users), add the Google account(s) you'll be using. Make sure the details are correct on the last page of the wizard and your consent screen will be configured.
- Now select Credentials from the side pane and then select Create Credentials. Select the OAuth Client ID option.
- Select Web Application as the Application Type.
- Register
<Cal.flare URL>/api/integrations/googlecalendar/callback. For Google login, also register<Cal.flare URL>/api/auth/callback/google, using your actual public origin. - The key will be created and you will be redirected back to the Credentials page. Select the newly generated client ID under OAuth 2.0 Client IDs.
- Select Download JSON. Copy the contents of this file and paste the entire JSON string as the
GOOGLE_API_CREDENTIALSWorker secret.
Add Google Calendar to the app store
After the D1 app-store seed is ported, populate app metadata using the Bun workspace command:
bun run --cwd packages/prisma seed-app-store
Enable the Google Calendar app, connect the intended account and choose its destination/conflict calendars.
Set GOOGLE_LOGIN_ENABLED=true when enabling Google login, and register its callback as described above.
Publish or verify the OAuth consent application when required for the users and scopes you intend to support; test-user access follows Google's consent configuration.
Obtaining Microsoft Graph Client ID and Secret
- Open Azure App Registration and select New registration
- Name your application
- Set Who can use this application or access this API? to Accounts in any organizational directory (Any Azure AD directory - Multitenant)
- Set the Web redirect URI to
<Cal.flare URL>/api/integrations/office365calendar/callbackreplacing Cal.flare URL with the URI at which your application runs. - Use Application (client) ID as the MS_GRAPH_CLIENT_ID server configuration value
- Create a client secret under Certificates & secrets and store its value as MS_GRAPH_CLIENT_SECRET.
- Grant the delegated scopes requested by the adapter:
User.Read,Calendars.Read,Calendars.ReadWriteandoffline_access, with consent appropriate to your tenant. - Connect the account and select its calendars. Teams conferencing requires an eligible work or school account.
Obtaining Zoom Client ID and Secret
- Open Zoom Marketplace and sign in with your Zoom account.
- On the upper right, click "Develop" => "Build App".
- Select "General App" , click "Create".
- Name your App.
- Choose "User-managed app" for "Select how the app is managed".
- De-select the option to publish the app on the Zoom App Marketplace, if asked.
- Now copy the Client ID and Client Secret to server-side configuration under the
ZOOM_CLIENT_IDandZOOM_CLIENT_SECRETfields. - Set the "OAuth Redirect URL" under "OAuth Information" as
<Cal.flare URL>/api/integrations/zoomvideo/callbackreplacing Cal.flare URL with the URI at which your application runs. - Also add the redirect URL given above as an allow list URL and enable "Subdomain check". Make sure, it says "saved" below the form.
- You don't need to provide basic information about your app. Instead click on "Scopes" and then on "+ Add Scopes". On the left,
- click the category "Meeting" and check the scope
meeting:write:meeting. - click the category "User" and check the scope
user:read:settings.
- click the category "Meeting" and check the scope
- Click "Done".
- Enable Zoom in Cal.flare, connect the account, and verify the meeting operations used by your event types.
Obtaining Daily API Credentials
- Open Daily.co and create an account.
- From within your dashboard, go to the developers tab.
- Copy your API key.
- Store the API key as the
DAILY_API_KEYWorker secret. - Set
DAILY_SCALE_PLANonly when your Daily account supports the features it enables, such as recording.
Obtaining Basecamp Client ID and Secret
- Visit the 37signals integrations dashboard and sign in.
- Register a new application by clicking the Register one now link.
- Fill in your company details.
- Select Basecamp 4 as the product to integrate with.
- Set the Redirect URL for OAuth
<Cal.flare URL>/api/integrations/basecamp3/callbackreplacing Cal.flare URL with the URI at which your application runs. - Click on done and copy the Client ID and secret into the
BASECAMP3_CLIENT_IDandBASECAMP3_CLIENT_SECRETfields. - Set
BASECAMP3_USER_AGENTto identify your application and support contact. Keep the OAuth client secret inBASECAMP3_CLIENT_SECRET.
Obtaining HubSpot Client ID and Secret
- Open HubSpot Developer and sign into your account, or create a new one.
- From within the home of the Developer account page, go to "Manage apps".
- Create a public OAuth application using the app-registration flow available to your developer account.
- Fill in any information you want in the "App info" tab
- Go to tab "Auth"
- Now copy the Client ID and Client Secret to server-side configuration under the
HUBSPOT_CLIENT_IDandHUBSPOT_CLIENT_SECRETfields. - Set the Redirect URL for OAuth
<Cal.flare URL>/api/integrations/hubspot/callbackreplacing Cal.flare URL with the URI at which your application runs. - Grant the adapter's scopes:
crm.objects.contacts.read,crm.objects.contacts.writeandcrm.objects.owners.read. - Click the "Save" button at the bottom footer.
- Connect the HubSpot account, then verify the expected contact and meeting effects with a synthetic booking.
Obtaining Webex Client ID and Secret
Obtaining ZohoCRM Client ID and Secret
- Open Zoho API Console and sign into your account, or create a new one.
- From within the API console page, go to "Applications".
- Click "ADD CLIENT" button top right and select "Server-based Applications".
- Fill in any information you want in the "Client Details" tab
- Go to tab "Client Secret" tab.
- Now copy the Client ID and Client Secret to server-side configuration under the
ZOHOCRM_CLIENT_IDandZOHOCRM_CLIENT_SECRETfields. - Set the Redirect URL for OAuth
<Cal.flare URL>/api/integrations/zohocrm/callbackreplacing Cal.flare URL with the URI at which your application runs. - In the "Settings" section check the "Multi-DC" option if you wish to use the same OAuth credentials for all data centers.
- Click the "Save"/ "UPDATE" button at the bottom footer.
- Enable the Zoho CRM app, connect the account and verify its intended booking effects.
Obtaining Zoho Calendar Client ID and Secret
Obtaining Zoho Bigin Client ID and Secret
Obtaining Pipedrive Client ID and Secret
Rate limiting
Rate limiting uses Unkey, as upstream does. Set UNKEY_ROOT_KEY to enable it. Without the key, rate limiting is off.
Payments and automation
- Stripe setup covers Connect OAuth and payment credentials.
- HitPay, PayPal, BTCPay and Alby retain their own provider settings and signature rules.
- Zapier and Make describe API-key connections and webhook setup.
The community baseline contains Stripe webhook stubs. Registering credentials does not replace those stubs with payment fulfillment. Check the port plan for the in-scope payment behavior.
Contributing
We welcome contributions! Whether it's fixing a typo, improving documentation, or building new features, your help makes Cal.flare better.
Important: Cal.flare is a community fork. Contributions to this repo do not flow to Cal.com's production platform. See CONTRIBUTING.md for details.
- Check out our Contributing Guide for detailed steps.
- Please follow our coding standards and commit message conventions to keep the project consistent.
Even small improvements matter — thank you for helping us grow!
Good First Issues
We have a list of help wanted that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
Contributors
Translations
Don't code but still want to contribute? help translate Cal.flare into your language.
Acknowledgements
Cal.flare is built on the foundation created by Cal.com and the many contributors to the original project. Special thanks to: