Tauri 2, React 19 and zustand on the front, Rust behind. Rust owns auth, all HTTP to Google, the SQLite store, the sync loop, recurrence expansion and timezone maths. TypeScript owns rendering and never talks to Google, which keeps the content security policy locked to ipc:. Week, day and agenda views, and no month view: it would be a second layout engine, and the fit and fold logic that makes a day fit the window without scrolling is the whole point of the app. Runs on macOS, Linux, Android and iOS. Desktop catches Google's OAuth redirect on a loopback port. A phone cannot, and Google rejects loopback for mobile client types anyway, so it redirects to a custom URI scheme and needs its own public OAuth clients, which docs/mobile.md covers. Refresh tokens are sealed with XChaCha20-Poly1305 in the app data directory on every platform, with no OS credential store in the picture. On a phone the chrome becomes a top bar and a bottom tab bar, overlays become sheets, hover affordances become taps, and dragging out an event waits for a long press. Navigation moves one day at a time everywhere, a swipe included.
2.8 KiB
Setup
The Google OAuth client
The app talks to Google with its own OAuth desktop client, which is not in this repo and cannot be. You need to make one before the app can connect to anything.
- In the Google Cloud console, enable the Google Calendar API on a project.
- On the OAuth consent screen, add the
https://www.googleapis.com/auth/calendarscope. It is a sensitive scope, so an unverified client shows an interstitial and caps at 100 users. That is fine for personal use and matters the day this ships more widely. - Create an OAuth client ID of type Desktop app. That type permits the loopback redirect, and Google allows installed clients any loopback port, so there is nothing to register. This client is for macOS, Linux and Windows only; phones need their own, see mobile.md.
- Save the downloaded JSON as
google-credentials.jsonin the repo root. It should match the shape ofgoogle-credentials.example.json: aninstalledobject withclient_id,client_secret,auth_uriandtoken_uri.
The real file is gitignored. The build embeds it when it is present and embeds the example when it is not, so a fresh clone builds and then tells you at runtime that Google Calendar is not set up yet, rather than failing to compile.
Building for a phone needs a second and third OAuth client, because Google will not accept a desktop client from Android or iOS. That, and the toolchain, is in mobile.md.
Refresh tokens never go to disk in plaintext and never into the SQLite database. They are sealed
with XChaCha20-Poly1305 in the app data directory, identically on all five platforms, by
src-tauri/src/google/secrets.rs. That file states what the encryption is worth where: on iOS and
Android the app sandbox is the real boundary and this is defence behind it, while on a desktop
anyone who can read your home directory can read the token. There is no OS credential store in the
picture and nothing will ever prompt you for keychain access.
Building
pnpm install
pnpm tauri dev
pnpm test runs the frontend suites, cargo test inside src-tauri runs the Rust ones. Both
should be green before anything is considered done.
Linux
Building needs libwebkit2gtk-4.1-dev, libgtk-3-dev, librsvg2-dev,
libayatana-appindicator3-dev and libxdo-dev. Build on the oldest baseline you intend to
support, which is Ubuntu 22.04 or Debian 12, because the resulting binary will not run on anything
older than the glibc it was linked against.
Where the data lives
~/Library/Application Support/studio.margin.calendar/ on macOS and
~/.local/share/studio.margin.calendar/ on Linux. The bundle identifier is deliberately distinct
from margin's, so the two apps never share a directory. Deleting that directory resets the app;
the accounts reconnect and sync pulls everything back.