--- title: How Calendite differs description: The handful of places Calendite breaks with the standard calendar model, and what that lets you do. order: 2 --- # How Calendite differs from other calendar apps Most calendar apps are the same app. They differ in typography and sync backends, but underneath they all inherit the same 1998 model: an event is a title, a start, an end, and a repeat rule chosen from a dropdown. Calendite breaks with that model in a handful of specific places. This page is about those places: what is different, and what it lets you do that you could not do before. Everything below is describable in one sentence: **Calendite treats a schedule as something you can compute with, not something you fill in.** > **Platform note.** Calendite targets Android, iOS and the web, but the three are not at > parity. Android is complete. On iOS and the web, several of the features described here > aren't available yet; each section says so where it matters. Read this page as "what > Calendite is on Android today". --- ## 1. Recurrence is a language, not a dropdown Every mainstream calendar exposes recurrence through a fixed form: *every N days / weeks / months / years*, maybe *on the 2nd Tuesday*, and then it runs out. If your schedule doesn't fit the form, you are told to create several events and keep them in sync by hand. Calendite's recurrence is a small text language. You type the schedule: | You want | You write | |---|---| | Every weekday except bank holidays | `WD, !BankHolidays` | | Every Saturday except the first two of the month | `Sat, !1Sat, !2Sat` | | The last weekday of the month (payday) | `-1__WD__OF__MONTH` | | US Thanksgiving | `4__Thu__OF__Nov` | | Friday the 13th | `13Month__&__Fri` | | The nearest weekday to Christmas | `WD~=25Dec` | | Mondays and Thursdays, Q1 only | `Mon, Thu, LIMIT(Jan.Feb.Mar)` | | Every full moon | `MOON(FULL)` | | Every Good Friday, without a holiday feed | `EASTER(GoodFriday)` | | Bin day, every other Tuesday | `Tue2` | Several of those are not merely awkward in Google Calendar or Apple Calendar: they are **not expressible at all**. There is no way to say "except bank holidays" in an RRULE, because RRULE has no notion of a named set of dates it doesn't already know about. You are never forced into the syntax. The repeat sheet offers the ordinary pickers, and the rule bar shows you what they compiled to, so the language is a ceiling, not a floor. The full syntax is documented in the [rule reference](/docs/rules-reference.html), which is generated from and verified against the rule engine itself. ### The bit people miss: order matters Rules are applied left to right: normal rules add dates, `!` rules remove them. So: ``` WD, !Fri ✅ every weekday except Friday !Fri, WD ❌ removes Friday, then adds all weekdays back, so Friday returns ``` Write inclusions first, exclusions last. --- ## 2. Rules can refer to your other events This is the part with no equivalent anywhere else. A rule can name a **tag**, an **event series**, or an external **feed**, and use its dates as raw material: | Rule | Means | |---|---| | `Tag(Work)` | every day carrying the `Work` tag | | `Event(Work)` | the occurrence dates of the single event series named `Work` | | `Feed(BankHolidays)` | dates supplied by an external feed you configured | Which unlocks things like: ``` -1__Tag(Work)__OF__MONTH ``` *"The last **working** day of the month"*, where "working day" means whatever **your** Work events say, including the shift patterns and the holidays you booked. If you take the last Friday off, the rule moves. No other calendar can express a schedule that is defined relative to another schedule. `Tag(Work)` and `Event(Work)` are deliberately different sets: one is every event sharing a tag, the other is one specific series, which is why you have to say which you mean. > These rules need a data source to resolve. On their own they produce no dates. --- ## 3. Inner Events: things that only happen inside other things A **stand-up** happens at 09:15 on work days. Not on Mondays, on *work days*. If Tuesday is a bank holiday, there is no stand-up. If you swap to a late shift, the stand-up moves with it. In a normal calendar you model this as a second recurring event on a weekday repeat, and then spend the rest of the year deleting the instances that shouldn't have happened. In Calendite, the stand-up is an **Inner Event** of Work. Its rule is implicitly intersected with its parent's, so it *cannot* occur on a day its parent doesn't. Cancel a work day and the stand-up goes with it, permanently and by construction. Inner events can be anchored **relative** to the parent rather than to the clock ("15 minutes after it starts", "30 minutes before it ends"), so moving the parent moves them. They render inside the parent block rather than competing with it for space: a darkened strip along the bottom of the parent, carrying one pill per inner event, positioned proportionally within the parent's span. Nesting is one level deep, and an inner event belongs to exactly one parent. --- ## 4. Variant Events: one thing, several shapes You work Earlys, Lates and Nights. It is one job, one tag, one colour, one set of notes, but three different sets of hours. Other calendars make you choose between three unrelated recurring events (and maintain each one separately forever) or one event you edit instance by instance. Calendite has **Variant Events**: named variations of a single series that differ in a handful of attributes (usually times, sometimes location or alerts) and share the rest. ``` Work ├── Early Shift 06:00–14:00 Mon2, Wed2, Fri2 ├── Late Shift 14:00–22:00 Tue2, Thu2 └── Night Shift 22:00–06:00 -1Sat ``` **How the base and its variants interact:** a variant supersedes the base **only on the days that variant itself fires**. On every other day the base still fires normally. So a base rule of `WD` with a `Night Shift` variant on `-1Sat` gives you weekdays *and* the last Saturday; the base is not switched off. Most inherited fields are resolved and stored when the variant is saved, not looked up live. Editing the base event's time, duration, colour, tag or location afterwards will **not** change variants that already exist. Alerts are the exception: they resolve live, so an alert added to the base later reaches existing variants. --- ## 5. Natural language, parsed on your phone Type *"every other Tuesday except bank holidays"* and Calendite turns it into `Tue2, !BankHolidays`. Fantastical and Google Calendar do this too, by sending your sentence to a server. Calendite ships the models and runs them locally: roughly 57 MB of quantised ONNX (a sequence-to-sequence translator for the rule itself, and a MiniLM sentence embedder for resolving names). Your phrasing never leaves the device, and it works on a plane. The name-resolution step is the interesting half. The translator can't know whether "karate days" means a tag or an event you own, so it emits a placeholder and the app resolves it against your own data by embedding similarity, offering you a choice when the match is ambiguous and asking you to rephrase when it finds no match. That's why `Tag(X)` and `Event(X)` are separate: the resolver has to pick, and it tells you when it can't. > **Android only for now.** On-device parsing isn't available on iOS yet. --- ## 6. Your notes are just Markdown files in a folder you choose Calendite's notes are not rows in a database. They are `.md` files on disk, in a folder tree you can point at your Obsidian vault: ``` Events/ ├── Past/ ├── Today/ │ └── Work/ │ ├── Description.md │ ├── .templates/ ← masters for repeating notes │ └── 2026-06-15 -- Sunday/ │ ├── Handover.md │ └── Stand-up/ ← inner event's own notes └── Future/ ``` No frontmatter, no proprietary metadata, no export step: the files *are* the storage. Occurrence folders are filed into Past / Today / Future buckets and re-filed nightly, so the tree stays navigable outside the app. A daily sweep moves folders between buckets using copy → per-file SHA-256 verify → delete, so an interrupted move can't lose a note. > **Android only for now.** The nightly sweep only runs against the app's private storage, > so if you point Calendite at an Obsidian vault the automatic re-bucketing does not run > there. The editor's rendered view uses plain CommonMark, so GFM tables, task checkboxes > and `~~strikethrough~~` display as raw text. --- ## 7. Any JSON on the internet can become a set of dates Bank holidays are the obvious case, but the mechanism is general. Give Calendite a URL and a JSONPath (there's a point-and-click builder if you don't want to write one), map which key is the title and which is the date, and you have a named category usable in any rule: ``` WD, !BankHolidays ``` Term dates, fixture lists, bin collections, a rota published as JSON: anything with dates in it becomes something your schedules can subtract. Feeds that describe the same day merge into one event rather than stacking up, and an optional *qualifier* field keeps distinct entries apart (so a bank holiday and its substitute day stay separate). > **Android only for now.** There is also no refresh schedule: feeds re-sync when you save > one and when you bring the app to the foreground. There is no `.ics` URL subscription; > the only calendar import is from the device's own calendar. --- ## 8. Sharing with no account, and no server that can read anything There is no Calendite account. There is no sign-up. Two people pair by **being in the same room**: one phone shows a QR code, the other scans it, both phones then display the same six digits and both people must confirm they match. That confirmation is a hard gate: the calendar key does not move until both sides tap it. After that, phones sync **directly over your Wi-Fi**. A relay server is available for when you're not on the same network, but it is **off by default** and you choose it explicitly in Settings: the Calendite one, your own deployment, or none at all. When a relay is used, it holds sealed blobs it cannot open. Every change is encrypted twice over, under independent keys (ChaCha20-Poly1305 then AES-256-GCM), with key agreement that is post-quantum hybrid: an attacker has to break both classical *and* lattice cryptography, not either. The relay learns traffic shape: which mailbox, when, how big. Not contents, not keys, not names. Hiding an event from a partner is enforced by cryptography rather than by their app's UI: for a hidden event, only the timing is ever encrypted under the shared key, so a modified client has nothing to reveal. It shows as a "Busy" block because that is all that was sent. > **Limitations.** Sharing is **Android only for now.** Revocation and hiding protect the > *future*: what a partner has already synced is theirs. The key agreement is hybrid post-quantum, so traffic recorded today can't be decrypted later by a quantum computer. [How the encryption works](/docs/privacy/how-the-encryption-works.html) covers the details. --- ## 9. The week view is rotated Every calendar puts days in columns and time down the page. A one-hour meeting therefore gets a sliver of width, and its title is cut to about four characters. Calendite's week view is **transposed**: dates run down the left, time runs across. An event's **width is its duration**, so a four-hour meeting gets four hours of room for its title. All seven days always fit the screen height; the time axis scrolls sideways. The follow-on is a set of tricks to keep labels readable no matter how tight things get. A block picks the best of five modes for the room it has: title with times, title alone, label spilled onto empty grid to the right, spilled left, or the title rotated 90° to read down the block. Long events keep their title pinned within view as you scroll, so you can always tell what you're looking at. --- ## 10. Not everything on a calendar is an event Most calendars have one noun and bend it to fit everything. Calendite has four [calendar items](/docs/everyday-use/calendar-items.html) and asks which you mean before it shows you a form: an **event** you attend, a **reminder** you do, a **deadline** you work towards, and a **time period** — a holiday, or time off — measured in whole days rather than clock hours. The distinction earns its keep because each kind then behaves differently. A deadline counts down to its moment and then counts up past it. A time period carries a start and end *date* where an event carries times, and draws as one continuous band across every day it covers. And a reminder is a thing you must *do*, not a span of time you must *attend*: no duration, never all-day, and its alert is mandatory — a reminder that never fires is pointless. Two reminder behaviours that ordinary calendars don't have: - **Recurrence anchor.** A repeating reminder either stays *on schedule* (dates come straight from the rule; completing late changes nothing) or repeats *after completion* (the next one is computed from when you finished). "Water the plants every 3 days" means 3 days after you last watered them, not every third calendar day. - **Do-not-interrupt.** A reminder that would fire during an event you're currently in defers itself until that event ends, rather than ringing over your meeting. When one rings you get **Done** or **Remind Me Later**, and "later" means a time you choose, not a fixed snooze. --- ## 11. Alert text is templated An alert's title and body can carry values from the event it belongs to, using `%` codes: ``` %t starts in %tm minutes ``` → *"Stand-up starts in 10 minutes"* There are 27 variables: the event title, time until or since (in several units and formats), duration, formatted dates and times, location, tag name and colour, and the current clock. Time-relative ones re-render live while an alarm is ringing, so the count stays true. An alarm can also carry an **app to launch**: a badge on the ringing screen that opens the thing the alarm is *for*. --- ## What Calendite is not - **It is not a team calendar.** Sharing is peer-to-peer between people, currently one partner at a time. There are no rooms, no invitations with RSVPs, no free/busy lookup across an organisation. - **It is not cross-platform yet.** Android is complete. iOS and web run the UI, but sharing, notes, feeds and on-device parsing aren't available there yet. - **It does not sync with Google Calendar.** It can import from the device calendar once; it is not a two-way client for someone else's calendar service. - **It is not simple.** Inner events, variants and a rule language are more concepts than most people need. The pickers cover the ordinary cases, and the rest is there when the ordinary cases stop working. --- ## Where to go next | To learn | Read | |---|---| | The full rule syntax | [Rule reference](/docs/rules-reference.html) | | What the domain words mean | [Glossary](/docs/reference/glossary.html) | | Getting set up | [Getting started](/docs/getting-started.html) | | Repeating schedules, from scratch | [Repeating events](/docs/everyday-use/repeating-events.html) | | Inner and variant events in practice | [Inner and variant events](/docs/everyday-use/inner-and-variant-events.html) | | Sharing and what it does with your data | [Sharing your calendar](/docs/privacy/sharing-your-calendar.html) | | Exactly what the app sends, and when | [What leaves your device](/docs/privacy/what-leaves-your-device.html) | | Building a repeat without typing a rule | [Building repeats](/docs/everyday-use/building-repeats.html) | | Something isn't working | [Troubleshooting](/docs/troubleshooting.html) |