Add Datenschutzerklärung, two blog posts, and a /uses colophon
- datenschutz.astro: DSGVO privacy policy (server logfiles, Hetzner as Auftragsverarbeiter per Art. 28, cookieless Umami, self-hosted fonts, data-subject rights, supervisory-authority complaint). - consts.ts: extract shared LEGAL entity data; Impressum now reads from it. - blog: "Why my calendar app has no internet permission" and "Open standards as a constraint, not a checkbox". - uses.astro: colophon of the site stack, Floret apps, and self-hosted infra. - Footer: add Uses + Datenschutz links alongside Impressum. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
src/content/blog/no-internet-permission.md
Normal file
67
src/content/blog/no-internet-permission.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Why my calendar app has no internet permission
|
||||
description: >-
|
||||
Calendula can't talk to the network — and that's the whole design. A look at
|
||||
building on Android's CalendarContract instead of reinventing a sync stack.
|
||||
pubDate: 2026-06-28
|
||||
tags: [android, architecture, calendula]
|
||||
draft: false
|
||||
---
|
||||
|
||||
Open Calendula's manifest and you'll notice something missing: there is no
|
||||
`android.permission.INTERNET`. The app physically *cannot* reach the network.
|
||||
For a calendar — a category of app practically synonymous with cloud accounts —
|
||||
that sounds like a missing feature. It's the opposite. It's the design.
|
||||
|
||||
## The usual shape of a calendar app
|
||||
|
||||
Most calendar apps own their data. They sign you into an account, pull events
|
||||
down over the provider's API, cache them in a private database, and reconcile
|
||||
changes with their own sync engine. That sync stack is the hard part: conflict
|
||||
resolution, recurring-event expansion, time zones, retries, token refresh. It's
|
||||
also the part that locks you in — your events live in *their* schema, reachable
|
||||
only through *their* app.
|
||||
|
||||
## The other option Android already gives you
|
||||
|
||||
Android ships a system calendar database, exposed through
|
||||
[`CalendarContract`](https://developer.android.com/reference/android/provider/CalendarContract).
|
||||
Anything synced to your device lands there: a CalDAV account via
|
||||
[DAVx5](https://www.davx5.com/), your Google calendar, a local on-device
|
||||
calendar, a read-only WebCal subscription. They all show up through the same
|
||||
content provider, with the same columns.
|
||||
|
||||
Calendula is a pure front-end over that provider. It reads events through
|
||||
`CalendarContract`, and when you create or edit something, it writes straight
|
||||
back. Whatever sync adapter put the calendar on your device picks the change up
|
||||
and pushes it out. There is **no own database and no reinvented sync stack** —
|
||||
so there is nothing for the app to phone home about.
|
||||
|
||||
## What you get for free
|
||||
|
||||
Dropping the network permission isn't a sacrifice; it's what falls out of the
|
||||
architecture:
|
||||
|
||||
- **Your data stays yours, and stays portable.** Events live in the platform's
|
||||
store and in your CalDAV account — not in a schema only Calendula understands.
|
||||
- **Privacy is structural, not a promise.** Zero telemetry and zero analytics
|
||||
are easy to claim. *No internet permission* is enforced by the OS: even if I
|
||||
wanted to exfiltrate your schedule, the app couldn't.
|
||||
- **Reminders still work** — Calendula delivers them itself as notifications,
|
||||
because Android delegates reminder delivery to the installed calendar app.
|
||||
- **Any account "just appears."** Add a new CalDAV account in DAVx5 and it
|
||||
surfaces in Calendula with no integration work, because the integration point
|
||||
is the OS, not a vendor API.
|
||||
|
||||
## The trade-off, stated honestly
|
||||
|
||||
A front-end can only be as good as the provider beneath it. Calendula doesn't
|
||||
add its own server-side features, and it relies on a sync adapter like DAVx5
|
||||
being installed to actually move bytes. That's a deliberate line: I'd rather put
|
||||
a thoughtful Material 3 Expressive interface on an open protocol than own a sync
|
||||
stack I'd inevitably get subtly wrong.
|
||||
|
||||
The same idea drives the rest of the [Floret family](/work) — Agendula is the
|
||||
exact same bet, made on the OpenTasks provider instead of the calendar one.
|
||||
Different content, identical philosophy: build the part that's worth building,
|
||||
and let open standards carry the rest.
|
||||
56
src/content/blog/open-standards.md
Normal file
56
src/content/blog/open-standards.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Open standards as a constraint, not a checkbox
|
||||
description: >-
|
||||
CalDAV, iCalendar, OpenTasks, DecSync — why I treat open standards as a hard
|
||||
boundary for what the Floret apps are allowed to do.
|
||||
pubDate: 2026-06-27
|
||||
tags: [open-standards, caldav, android]
|
||||
draft: false
|
||||
---
|
||||
|
||||
"Supports open standards" usually means a feature in a list — an export button,
|
||||
an import dialog, an optional CalDAV setting buried three screens deep. For the
|
||||
[Floret apps](/work) it's the other way round: open standards are the boundary,
|
||||
and everything that would step outside them is simply out of scope.
|
||||
|
||||
## The lane
|
||||
|
||||
The standards are deliberately boring and well-proven:
|
||||
|
||||
- **[CalDAV](https://datatracker.ietf.org/doc/html/rfc4791)** and
|
||||
**[iCalendar](https://datatracker.ietf.org/doc/html/rfc5545)** for calendar
|
||||
events — the protocol and the data format the rest of the ecosystem already
|
||||
speaks.
|
||||
- **[OpenTasks](https://github.com/dmfs/opentasks)' `TaskContract` provider**
|
||||
for tasks, which stores CalDAV VTODOs on the device.
|
||||
- **[DecSync](https://github.com/39aldo39/DecSync)** for peer-to-peer sync
|
||||
without a server in the middle.
|
||||
|
||||
Calendula reads and writes events through Android's `CalendarContract`; Agendula
|
||||
does the same over the OpenTasks provider. Neither app owns a database. The sync
|
||||
adapter you already trust — DAVx5, SmoothSync, DecSync — moves the bytes.
|
||||
|
||||
## Why make it a hard boundary
|
||||
|
||||
Treating the standard as a constraint changes which decisions are even on the
|
||||
table. A proprietary task service with a slick API is permanently off the
|
||||
roadmap — not because it's bad, but because integrating it would mean owning a
|
||||
sync stack and tying your data to one vendor's schema. The moment an app starts
|
||||
reconciling its own copy of your data against someone's cloud, the simplicity
|
||||
that made it trustworthy is gone.
|
||||
|
||||
The constraint also keeps the apps **reproducible**. There are no secret API
|
||||
keys to embed, no SDKs that pull in closed dependencies, nothing that would stop
|
||||
an app from building clean for [F-Droid](https://f-droid.org/). What goes in is
|
||||
exactly what you can read in the source.
|
||||
|
||||
## The cost, and why it's worth paying
|
||||
|
||||
Living inside the standard means some things are genuinely harder. Provider APIs
|
||||
have rough edges; recurrence rules and time zones in iCalendar are a deep well;
|
||||
and you inherit whatever the underlying sync adapter does or doesn't support.
|
||||
You don't get to paper over those gaps with a server you control.
|
||||
|
||||
That's the right trade. An app built on an open standard is one you can leave
|
||||
without losing anything — your events and tasks were never hostage to it in the
|
||||
first place. The interface is mine to get right; the data was always yours.
|
||||
Reference in New Issue
Block a user