Skip to content

Calendar and scheduling

Control Center connects to Google Calendar so your day sits alongside your fleet. It syncs your events, shows them in month, week, day and agenda views, warns you before a meeting starts and turns any event into a recorded, summarized meeting.

Almost nothing. The integration pulls events in and renders them; it never creates, edits, or deletes calendar entries. The one write it can make is your own attendance response and even that is constrained by the scope the connect flow requests.

Events are synced into the workspace’s own database on the server (<dataDir>/<workspaceId>/workspace.db) and rendered by every client over RPC. No client holds a Google credential.

Calendar accounts are workspace-scoped, like everything else in Control Center. Each workspace connects its own Google account (or several) and one workspace’s events and tokens are never visible from another. Switching workspaces switches which calendars you see.

A workspace can connect multiple Google accounts, such as a work account and a personal one and their events are merged into the same views.

The headless cc_server has no browser to catch an OAuth redirect. So it does not use one: it authorizes with the device-code grant (RFC 8628). The server shows a short user code and a Google URL, you approve on any device that has a browser and the server polls until you do. A headless install can connect an account precisely because there is no redirect endpoint to host.

The consequence to know before you bring your own credentials: the client type must be Google’s “TV and Limited Input devices”. Google only grants an allow-listed set of scopes to that client type and a project where Calendar is not permitted for it fails with invalid_scope — you would need a loopback or web client instead.

Tokens never leave the server. They are written to the server’s secrets.json in its data directory, keyed google_google:<workspaceId>:<email>, alongside the databases and under the same host-filesystem trust boundary. The server refreshes access automatically and, when Google permanently invalidates an account (a revoked or long-expired session), publishes CalendarAuthExpired and surfaces a reconnect banner instead of failing silently.

A build from source ships no Google credentials at all — you supply your own client id and secret. Official release builds bake in a non-confidential device-code client so the connect flow works out of the box; the server stores only a marker referencing it, never a copy and a --google-client-id you supply always takes precedence.

For running your own Google project, see Connect a Google Calendar.

The server pulls a rolling window of each connected account’s events — every calendar on the account, not just the primary — every two minutes, plus on demand (start, connect, manual refresh). The short cadence is affordable because the sweep is incremental: each calendar carries its own sync token, so an unchanged calendar costs one near-empty response rather than re-fetching a five-month window.

Recurring events are expanded server-side, so no client ever parses a recurrence rule. Navigating to a month outside the current window lazily loads that range. Deleted or moved-out-of-range events are reconciled away on the next sync.

View What it shows
Month A full month grid (powered by the kalender package)
Week A week’s timed events in a time-grid
Day A single day’s timed events in a time-grid
Agenda A scrollable list of upcoming events

Open the calendar at /calendar; selecting an event opens its detail at /calendar/:eventId. The view you pick is stored in that client’s own preferences, so it does not follow you to another device.

A per-minute scheduler scans for events starting within a configurable lead window and fires a “meeting starting soon” notification (a MeetingStartingSoon domain event). The notification carries the join URL when one is present and clicking it opens the event detail. Alerts are de-duplicated against the event’s alertedAt, so the same meeting never alerts twice, even across restarts and an event whose start passed while the app was closed never replays.

Two limits are easy to trip over. The scheduler runs client-side on the desktop and only for the workspace you currently have open — a workspace you are not in raises no alert. And the web build has no OS-notification pipeline at all, so it raises none either.

From a calendar event you can start a meeting recording seeded with the event’s title. Control Center stores a one-to-one link between the recording and the event, so the meeting’s notes and the calendar entry stay connected without either feature depending on the other.

Start recording & link is desktop-only. On the web client the action reports that recording is unavailable — even though the meetings screen’s own record flow does work in the browser.