exceptwhen

Privacy

exceptwhen stores account, schedule, billing, and feed data to generate, explain, and publish calendar feeds.

Retention is not settled yet

The current code has delete functions for accounts and schedules, but it does not define how long cancelled account data is kept and does not run an automatic purge on cancellation. That retention decision still needs the owner before this can be a complete privacy policy.

Account and login data

Customer accounts store an account id, email address, creation time, and optional disabled time. Login records store the email address, account id, principal type, password verifier, creation time, and optional disabled time. Passwords are stored as scrypt verifier strings, not as plaintext.

Account sessions store a public session id, account id, email address, principal type, token digest, creation time, expiration time, and optional revocation time. The session token secret is carried in the browser cookie and is stored by the daemon only as a SHA-256 digest.

Password reset data

Password reset requests store a reset id, account id, email address, token digest, creation time, expiration time, and optional use time. The reset secret is returned to the web tier for delivery and is stored by the daemon only as a SHA-256 digest. If file-based reset delivery is configured, the outbox record contains the recipient email address, reset URL, expiration time, and delivery record id.

Schedule and feed data

Schedules store an account id, schedule id, slug, title, authored source text, creation time, update time, and optional archive time. The authored source can include the names, rules, exceptions, times, notes, and other facts a customer writes into the schedule.

The store also keeps compiled schedule JSON, fact snapshot JSON, evaluation output JSON, source fetch records, feed tokens, and feed configuration. Feed configuration includes visible states, event summary text, calendar name, display zone, UID settings, explanation base URL, granularity, and optional alarm settings.

Published feed URLs

A feed token is the access control for public calendar feed reads. Anyone who has an active feed URL ending in `.ics` or `.json` can read that published feed without signing in. Revoking or expiring the token stops future reads, but it cannot remove events that a calendar app already cached.

A feed token on an account without an active Live Calendar subscription does not serve schedule events, and the one-time download does not change that. The URL answers with a notice instead, so minting a token does not by itself make a schedule readable by anyone holding the link. Events begin being served when the account has an active Live Calendar subscription, and once they have been served the sentence above applies to them.

When a subscription is past due or canceled after its paid period, the account can still see schedules and preview stored calendars. It cannot edit, mint feed tokens, change feed settings, publish, serve a feed, or export until access is restored. A subscriber URL that already exists returns the previously published events with a stopped-updating notice.

The public frontend Caddy config redacts feed tokens from access logs. Password reset tokens are designed to travel in the URL fragment, which the server does not receive, and the same config redacts a reset token if it ever appears in a password reset query string.

Billing data and Stripe

Checkout is handled by Stripe. When checkout starts, exceptwhen sends Stripe the selected plan, account id, customer email address, success and cancel URLs, and metadata used to reconcile the checkout back to the account. Checkout asks Stripe to collect a billing address.

Stripe webhook events are verified before they are stored. Billing records can store the account id, billing state, plan, processor name, Stripe customer id, Stripe subscription id, Checkout Session id, current period end, last Stripe event id and event time, update time, and the country stated at checkout. Billing event records store the Stripe event id, processor, event type, account id when known, event creation time, handling outcome, and observation time.

Route access

Public identity routes are reachable without the `/admin/account` gate. Registration and login are protected by email and password checks. Customer logout is protected by the customer session bearer. Password reset request, revoke, and reset routes are protected by email validation, one-time reset token digests, expiration times, and use timestamps.

Account-scoped `/admin/account/...` routes are gated by a session for the same account or by the configured internal bearer token. Operator-only mutations bootstrap accounts and record Stripe billing events. Other account-scoped mutations create or update schedules, create or revoke feed tokens, publish schedules, and update feed configuration.