calviewsign in
§ privacy

Privacy policy.

Venn Labs LLC operates cal, a tool for merging iCal feeds - including feeds built from calendars you connect through Google - into one composed feed. This policy explains what we collect, why, and what happens to it. Effective 29 September 2026.

Who we are and what this covers

cal is operated by Venn Labs LLC. This policy covers the cal web application and its feed endpoints (/ical/<id>, /virtual/<id>), and applies to everyone who signs in, connects a Google account, or subscribes to a feed we serve.

What we collect

Depending on how you use cal, we collect:

  • Account email addresses - when you request a sign-in link, so we can send it and recognise you on return. One account can hold several addresses: you can add another from your account page, and we send a confirmation link to it so that only somebody who can read that inbox can attach it. Any confirmed address signs you in, and calendars shared with any of them are yours. If an address you add already belongs to another cal account, we offer - at your request, and only after you confirm - to merge that account into this one: its calendars, availability calendars, Google connections and addresses move across, its feed addresses are unchanged, and the absorbed account is deleted.
  • Google account details - if you sign in or connect with Google, we receive your Google account id (sub), email address, name, and profile picture from Google's ID token. You can attach more than one Google account, each with its own permissions, and disconnect any of them on its own. Signing in with a Google account we do not recognise does not create an account for you: we ask first.
  • OAuth tokens - if you connect Google, we store the access and refresh tokens Google issues, so we can read your calendars without asking you to sign in again each time.
  • Session cookie - a random token that keeps you signed in between requests.
  • iCal feed URLs - any external calendar addresses you add as sources to an aggregate.
  • Virtual calendar rules - the weekly availability rules you configure for a rule-generated calendar.
  • Calendars you choose to import - if you connect Google, the specific calendar(s) you select from your Google account to include as a source. We do not import calendars you have not selected, and what we read from the ones you do select depends on which tier you granted: availability only, or availability plus event details.
  • Appointments booked on your page - if you publish a booking page, we store what each visitor gives us in order to make the appointment: their name, their email address, the time zone their browser reported, any note or answers they typed, and the time they chose. We use it to confirm the appointment to both of you, to send the visitor a reminder the day before, and to let them move or cancel it from a link in their email. It is never used for anything else and is never sold or shared. Visitors do not need an account with cal to book, and we do not create one for them. We keep it while the appointment exists, and for 90 days after it is cancelled, after which it is deleted automatically. If the appointment is moved, the record it replaces keeps only the times - the visitor's name, address, note and answers are removed from it at the moment of the move, and live on only the appointment that stands. If you chose to have your booking page add appointments to one of your Google calendars, the visitor's name, email address and note are also written into that event in your calendar, and we record which of your Google calendars each appointment was added to.
  • Structured server logs - each request is logged with a request id, method, path, status, and timing. Before anything is logged or stored, we strip out any field whose name looks like a token, cookie, password, secret, or session value, and reduce any URL-shaped value down to just its hostname - so a feed URL with credentials in its path, for example, is recorded only as the domain it points to.

Google user data and the Limited Use policy

Google access comes in three tiers, and one further permission that sits beside them. Each one is a separate consent screen that you start yourself, and none of them implies another.

  • Sign-in identity - the openid, email and profile scopes, just enough to identify you and sign you in. Signing in never reads a calendar.
  • Free/busy access - the https://www.googleapis.com/auth/calendar.calendarlist.readonly and https://www.googleapis.com/auth/calendar.events.freebusy scopes, requested only when you choose to connect a Google calendar. The first lets us list the calendars you are subscribed to, so you can pick which ones to include; the second lets us read when those calendars are busy. At this tier cal can see when you are busy, never what the events are - we read availability through Google's free/busy endpoint, which returns start and end times and nothing else.
  • Event details - the https://www.googleapis.com/auth/calendar.events.readonly scope, requested only if you choose to include event details. It is what lets a full-detail feed carry event titles, descriptions and locations. If you never choose it, cal never asks Google for any of them.
  • Adding appointments - the https://www.googleapis.com/auth/calendar.events.owned scope, requested only if you choose to have a booking page add the appointments people book with you straight into one of your own Google calendars, instead of only emailing you an invitation. You pick the one calendar, and only calendars you own are offered - never one somebody else has shared with you. cal uses it to create an event when somebody books, move it when they reschedule, and delete it when the appointment is cancelled; it only ever changes or deletes events it created itself, each marked privately as belonging to your booking page. It reads back only those same events, to show you on your booking page when you have moved or deleted one in Google Calendar. Although Google's scope would allow it, cal never uses this permission to read any other event in your calendar, and it does not turn on the event-details tier.

You can withdraw the event-details tier on its own, without disconnecting cal from Google, by removing that permission at myaccount.google.com/permissions. The next feed built from that account falls back to free/busy blocks on its own: when Google refuses the event read, caldrops the event-details scope from its own record of your grant and reads your availability instead.

Accounts connected before these tiers existed hold the older https://www.googleapis.com/auth/calendar.readonly scope, which covers both tiers at once. The next time such an account goes through either of the two calendar consent screens above, cal discards that older scope and keeps only the narrower ones granted on that screen - so reconnecting at the free/busy tier leaves an account that can no longer read event details. Signing in on its own changes nothing.

You can stop a booking page adding appointments at any time by pointing it back at email in its availability panel. Appointments already added stay in your calendar, and cal still moves or removes them there if they are rescheduled or cancelled. If you delete your cal account, we first delete the events we added for appointments that had not happened yet, then revoke our access.

We read your Google calendar list and your calendar data only to build the feeds you have configured, and we write to your calendar only the appointments your booking pages take. We do not use Google user data for advertising, we do not sell it, and we do not share it with third parties, except that a feed you have chosen to make public serves exactly the events, at exactly the detail level, you configured for it - and except to our hosting provider, Railway, which processes data on our behalf to run the application. Employees or contractors do not read Google user data except where necessary for security, to investigate abuse, to comply with the law, or with your consent. We never use Google user data to train or improve artificial intelligence or machine learning models, generalised or not.

cal's use and transfer of information received from Google APIs to any other app will adhere to the Google API Services User Data Policy, including the Limited Use requirements.

How tokens are protected

  • OAuth access and refresh tokens are encrypted at rest with AES-256-GCM before they are stored. The encryption key is held only in server configuration, never in the database.
  • Your session cookie is a random value; only its SHA-256 hash is stored on our side, so the cookie itself cannot be reconstructed from the database.
  • Access tokens are refreshed automatically on the server, shortly before they expire, so a fresh token is always used and stale ones are not kept around longer than necessary.
  • All traffic between your browser, cal, and Google is sent over HTTPS.

Public feeds

A /ical/<id> or /virtual/<id> address is a bearer capability: anyone who has the URL of a public calendar can read what it serves, without signing in. What they see is controlled by the privacy mode you choose for that calendar - full detail, opaque busy blocks, or busy blocks merged into contiguous runs - so pick the mode that matches who you are sharing the link with.

Calendars set to private or shared visibility have no public feed URL; they can only be used by you, or by people you have explicitly shared them with. When one calendar is composed into another, it is anonymised by its own privacy mode before the composing calendar ever sees it, so composing a calendar can only make information more private, never less.

Retention and caching

  • Fetched feed bodies, and calendar events read from Google, are cached in server memory for up to 5 minutes to avoid refetching on every request.
  • Sessions last 30 days, then you are signed out.
  • Sign-in links (login tokens) expire after 15 minutes, or immediately once used.
  • Structured log events are kept for a configurable retention window, 30 days by default, then deleted.
  • A cancelled appointment - and everything the visitor told us to make it - is deleted 90 days after the cancellation. A record superseded by a move is deleted 90 days after it was superseded, and carries no visitor details in the meantime. The links that let a visitor move or cancel an appointment expire 30 days after they are issued, or a day after the appointment itself, whichever is later.
  • Deleting a booking page, an event type, or your whole account cancels every appointment on it that has not happened yet, and tells the people who booked them before the records go - so nobody turns up to an appointment that quietly stopped existing.
  • Disconnecting Google from your account deletes your stored tokens and any calendar sources built from that account immediately, and we make a best-effort attempt to revoke the grant at Google at the same time - though you should also check your Google account permissions to confirm.

Your choices and deletion

  • Disconnect Google at any time from /app/account.
  • Delete any calendar you own from /app.
  • Revoke cal's access to your Google account directly at myaccount.google.com/permissions.
  • If you booked an appointment with somebody, the link in your confirmation email cancels it, and cancelling deletes your details after the 90-day window above. You can also ask the person you booked with to cancel it for you.
  • To request deletion of your whole account and everything associated with it, contact us at hello@vennlabs.dev.

Cookies

We set two cookies, both essential and both httpOnly:

  • cal_session - keeps you signed in, for up to 30 days.
  • cal_google_oauth - a short-lived cookie (10 minutes) used only while you are in the middle of a Google sign-in or connect flow, to carry state safely across the round trip to Google.

We do not set advertising or analytics cookies, and cal does not embed any third-party analytics or advertising scripts.

Children

cal is not directed at children, and we do not knowingly collect personal information from children.

Changes to this policy

If how we handle data changes, we will update this page and move the effective date above. We do not expect to make that kind of change without good reason, given what this page promises about Google user data.

Contact

Questions about this policy, or about your data, can go to contact us at hello@vennlabs.dev.