Tags give the ability to mark specific points in history as being important
-
v2.2.4
protectedRelease: hyper-calendar 2.2.42c778fca · ·hypercal 2.2.4 Tasks grow into a full app with lists, subtasks, repeats, reminders and CalDAV sync, availability and booking links for people without an account, a history for every event, a list of your signed-in sessions, Prometheus metrics, and a new app frame with a side panel of shortcuts. **Tasks.** Tasks live in lists, each with its own colour and default reminders, and can have subtasks, a status, a start and due date, a priority, categories and a repeat (from the due date or from completion). Reminders arrive by push and on Android. Tasks show in every calendar view and can be dragged onto a date. The new Tasks app manages them, and a task list can be docked beside the calendar. Each list is its own CalDAV collection, VTODOs round-trip losslessly, and a list can be linked to a remote CalDAV task list and kept in sync. The MCP endpoint understands lists, subtasks and repeats too. Database migration 0050 adds task lists and the new task columns; done tasks become COMPLETED. **Availability and booking links.** Share a link that shows only when you are busy, as a web page or as a VFREEBUSY feed for calendar apps, with no titles or calendar names. A booking link goes further: visitors without an account pick an open slot from your availability rules, and the event lands in a calendar you choose. Concurrent requests for one slot produce exactly one booking. Visitors get a link to cancel from any device, and your own CalDAV clients see them as an attendee. Set working hours and links up in Settings > Availability; admins can switch the feature off. Migrations 0043 to 0045 add the tables. **Busy time.** Each calendar can opt in or out of counting as busy. By default subscriptions and birthdays no longer count, and accepted or tentative invitations do. With people selected, Find a time suggests the next times you are all free. Migration 0042. **Event history.** Every change to an event is recorded, whichever way it came in (app, CalDAV, MCP, extension, app password), and shown in the editor, where any earlier version can be restored. Removed attachments are kept while a version can still restore them. History and the Trash are pruned hourly: admins set the caps (50 versions, 180 days of history, 30 days in the Trash by default) and each account can keep less in Settings > Data. Migration 0047. **Sessions.** Settings lists where you are signed in, by browser and system, with when each session was last used, and signs any of them out. Migration 0048. **App frame and side panel.** Contacts, settings and admin open as pages in one app frame, and Tasks and scheduling polls are apps beside the calendar. The calendar's actions sit as grouped pills in the top bar. On desktop a side panel on the right opens Contacts, Find a time, Tasks, Polls and Invitations; Settings > Display > Side panel orders or hides it. **Smaller things.** A default event length per calendar (migration 0046), a choosable snap step for drags and keyboard nudges, the sidebar remembers its state per device, and confirmations that guard undoable actions can be turned off in Settings > Confirmations. **Self-hosting.** - `GET /metrics` serves Prometheus metrics: HTTP requests by route, sync passes, DAV requests, subscription fetches, reminder lag and deliveries, backup age and failures, database size and process gauges. It answers 404 until `METRICS_TOKEN` (at least 32 characters) is set. `METRICS_LISTEN` serves it on a second listener without a token; a non-loopback address also needs `METRICS_LISTEN_ALLOW_PUBLIC`. See the monitoring docs. - `GET /healthz` answers 503 when the database does not, and the image's HEALTHCHECK uses it. - `RATE_LIMITS` overrides any named rate limit as `name=max/seconds`, comma-separated. Unknown names fail the boot. - Linked calendars and address books remember their last successful sync (migration 0049). Migrations 0042 to 0050 run on first boot. **Fixes.** Offline edits to one occurrence merge against what the client saw, tied search results keep a stable order, Find a time no longer offers the repeat of an hour before availability opens, and an event moved with the keyboard stays selected when cards slide under the pointer.
-
v2.2.3
protectedRelease: hyper-calendar 2.2.398c00e42 · ·hypercal 2.2.3 Select several events and act on them at once, a restyled event card and editor, an admin dashboard, instances that keep accounts apart, Android widgets and shortcuts, a rebuilt Firefox extension, and a much wider MCP endpoint. **Selecting several events.** In week, day, month and schedule, Ctrl-click (⌘-click on a Mac) events to select them, or Shift-click a run of them. A bar then moves them to another calendar, recolours, tags, deletes or exports them as one .ics file. In week and day, Shift with the arrow keys moves the whole selection in time. Each change is one step for Undo, and repeating events ask once which occurrences to change. Search results can be selected the same way, and "Select all N results" reaches the ones not loaded yet. Deleting more than 20 events asks you to type how many first. **Tags.** Give an event tags in the editor. They travel in .ics files as `CATEGORIES`, and search filters by them. Database migration 0040 adds the column. **Event card and editor.** Hovering or clicking an event shows one card with Edit, Delete, More and Close on top, then the time, how it repeats, location, description, attachments, reminders and calendar. The editor puts date and time on one line, has an "All day" checkbox and a Repeat dropdown worded from the start date ("Weekly on Wednesday"). Its More menu adds Duplicate, Copy, Export and Share, and Ctrl+D and Ctrl+C work inside it. Context menus gain icons, shortcut hints and a colour grid, and the sidebar, account menu, search and command palette are restyled. **Admin dashboard.** Administration moves out of Settings into a page of its own, for admins only, from the account menu: - An overview with accounts, activity, storage, signups per month, the accounts storing the most, and the server's memory, CPU and uptime. - Users as a sortable table or cards, with search and filters. Open an account for its storage and counts, deactivate or reactivate it, or sign it out everywhere. - A deactivated account cannot sign in by any path (password, passkey, CalDAV, MCP, extension), and its feeds, reminders and push notifications stop. - Maintenance lists the database snapshots, takes one on demand and downloads them. Database migration 0039 adds the deactivated and last-seen columns. **Keeping accounts apart.** A new instance switch, "Keep accounts apart", for an instance whose users must not be linked. While it is on, shares, invitations, attendees, free/busy and the user directory are switched off on the server, and the app hides them. Existing shares are kept and come back when it is turned off. `ISOLATED_USERS=1` holds it on from the deployment. **Android.** Agenda and Week widgets for the home screen, launcher shortcuts (New event, Today, Search, and the next event) and a quick-settings tile for a new event. The widgets keep no wake lock and blank out after sign-out. Drawers and full-screen pages no longer slip under the system bars. **Firefox extension.** Rebuilt in Material 3 with hypercal's themes. It adds Agenda, Day, Month and Tasks views, event details with RSVP, edit, delete and undo, tasks, search, an invitations inbox, quick add (Alt+Shift+N), and reminders that wait in the popup with Snooze and Dismiss. **MCP.** Besides events, the endpoint now covers tasks, attendees and RSVP, attachments (read only), calendars, contacts and availability, plus agenda resources and workflow prompts. App passwords can limit MCP to chosen areas, each off, read, or read and change. A password with areas works on /mcp alone. Older passwords keep exactly what they had. Database migration 0041 adds the columns. **Fixed.** - The Repeat editor's preview no longer drops a date that falls in the hour the clocks skip. - The server refuses JSON request bodies with more than 200,000 values before validating them. This works around CVE-2023-54404 in zod, for which no fixed release exists yet, where one large request could exhaust the server's memory. -
v2.2.2
protectedRelease: hyper-calendar 2.2.27bb183c3 · ·hypercal 2.2.2 Event templates, a command palette and a settings backup. Also a redesigned contacts page, week cards that fit their text, preferences that follow your account, files linked from Nextcloud, lossless daily backups, and account management for admins. **Templates.** In the event editor, open "More options" and choose "Save as template…". A template keeps the title, length, calendar, colour, reminders, location and description, and optionally the guests. Start a new event from one with the + button, by right-clicking or long-pressing an empty slot, or from the all-day strip. Settings, Templates renames, reorders and deletes them. A template whose calendar is gone or read-only uses the default calendar. **Command palette.** Press Ctrl+K (⌘K on a Mac) to create an event, start one from a template, jump to today or another view, or open search, contacts, settings, templates or the shortcuts. The key can be rebound like the other shortcuts. **Settings backup.** Settings, Data, Settings backup saves your preferences, default reminders, custom theme, templates and rebound shortcuts to a file, and restores them on this or another device. With a server, week start, time format, working hours, default view, density and the other display preferences now follow your account across devices on their own. **Contacts.** The page is redesigned: favorites on top, A to Z sections with a letter rail, filters as removable chips, sorting by first or last name or recency, and a sortable table view on wider screens. Select contacts with checkboxes, Shift-click or Ctrl-click to star, label, move, export, merge or delete them at once, with Undo. A label sidebar filters by label or book and renames or deletes a label everywhere. **Week view.** Cards size their text to their width, and show the time only when a line is free for it. Overlapping events take a free column before they overlay one another, and widen into columns that are free for their time. Titles hyphenate in your language. **Nextcloud.** Connect your Nextcloud in Settings, Connectors, and choose a folder. The editor then offers "From Nextcloud" next to "Attach file". The event links to the file rather than copying it, and finds it again if it is moved or renamed. Database migration 0038 adds the tables for it. **Exports and backups.** - The whole-account export now carries tasks, subscriptions and your profile, and optionally attachments ("Export everything with attachments"). - Daily server backups are .hypercalendar files instead of .ics, so a restore brings back colours, reminders, tasks, contacts and subscriptions. Older .ics backups still restore. `BACKUP_KEEP_DAYS` and `DB_BACKUP_KEEP` set the retention. - An encrypted calendar's menu offers "Export decrypted…", decrypted in the app with your key. - The Android app reminds you to export when two weeks pass without one, since your calendar lives only on the phone. **Accounts and admin.** - Deleting your account asks for your password, and removes everything, backups on disk included. - Two-factor secrets are sealed with `CALDAV_SECRET` when it is set. - Admins can export and import the instance settings, see every account, grant or remove admin rights, and delete accounts. - The server refuses to start on a database a newer release migrated, and checks each database snapshot before keeping it. Database migrations 0036 and 0037 add the template and synced preference columns. **Fixed.** - Restoring a whole-account backup no longer adds a second copy of subscribed and birthday events. - The birthday year field keeps what you type. - Week shortcuts no longer fire under the contacts, polls or other panels. - Signing out forgets the account's cached settings, so the next account in the same tab no longer sees them. - The container image carries perl-base 5.36.0-7+deb12u4, which fixes CVE-2026-13221, CVE-2026-42496, CVE-2026-8376 and others. - All npm audit advisories in the build tooling are cleared, and Electron is 44.6.0. -
v2.2.1
protectedRelease: hyper-calendar 2.2.112c45b14 · ·hypercal 2.2.1 Contacts: an address book that syncs with CardDAV, and a birthdays calendar built from it. Also calendar and contact files opened from other apps, typed times in the editor, a full-page Settings, easier resizing on touch, and fixes to CalDAV sync and live updates. **Contacts.** Open the account menu, then Contacts, or press Shift+C. The page lists your contacts A to Z, with the next 30 days' birthdays and anniversaries at the top, and searches names, emails, phone numbers and labels. A contact holds several emails, phones, websites and addresses, dates with or without a year, a photo, labels and notes. The sidebar shows the coming week's birthdays. Import and export as CSV or vCard, with a preview before an import. The app finds likely duplicates and merges them. Contacts are part of a whole-account .hypercalendar backup. **CardDAV.** Link an address book to a CardDAV server such as Nextcloud, Radicale or Baïkal in Settings, Contacts, and it syncs both ways. Edits made on both sides are merged field by field. The other direction works too: switch on "Share over CardDAV" for a book, and DAVx5, iOS or Thunderbird can sync it from hypercal on the same /dav address and app passwords as your calendars. **Birthdays calendar.** Add it from the "+" menu in the calendar list. It shows birthdays, anniversaries or other dates from the books you pick, says how old someone turns, and can remind you the day before. You change a birthday on the contact, so the calendar itself is read-only. Database migration 0035 adds the tables for contacts and address books. **Files from other apps.** On Android, the app is offered under "Open with" and in the share sheet for .ics and .vcf files. The desktop app opens .vcf files too, and dropping one on the calendar imports it. **Editor and Settings.** - With a mouse, the start, end and due times are typed straight into the field, with a list of quarter hours. - Changing a recurring event asks which events to change with a radio group and OK, so one stray tap no longer rewrites the whole series. - Settings is a full page, and the open section is in the address, so a reload keeps it and it can be linked to. **Week view.** A selected event has an outline in the theme's accent colour. On touch, long-press and release an event to show its resize knobs, on opposite corners. The grid scrolls to show both, and when an edge is off screen its knob stays at the edge of the grid. An edge now moves as far as your finger does, instead of jumping to it. **Polls.** Times are larger and easier to read. The clock is 24-hour in Europe, with a switch. Drag across slots to answer them all at once, and meters show how the votes stand. **Privacy.** A new privacy page says what the app stores and which connections it can make. About links to it. The desktop app's check for new versions is now off by default; turn it on in Settings, General. **Fixed.** - Live updates could stop reaching your other open tabs after one of them closed or reconnected. - CalDAV sync now fetches only what changed since the last pass, and edits made on both sides reach the server instead of staying local. - Keyboard shortcuts work in the Android app with a hardware keyboard, and Escape closes a dialog without also going back. - Temperatures and rain are readable in the weather view on phones. - The favicon's date fills the tile. - The container image is rebuilt on current Debian packages, which fixes CVE-2026-103111 in libpcre2.
-
v2.2.0
protectedRelease: hyper-calendar 2.2.0a72ee47c · ·hypercal 2.2.0 Scheduling polls: find a time with people who have no account. Also a calendar that opens without the server, short events that move with the mouse, and license notices in every bundle. **Scheduling polls.** Open the account menu, then Scheduling polls, and describe the times you can offer: a range of dates, which weekdays, one or more time windows a day, and one or more slot lengths, back to back or every 15, 30 or 60 minutes, with an optional break. A preview grid shows every slot; click one to leave it out, skip a whole day, or add a slot by hand. Whole-day slots work too. Then set the rules: yes/no or yes/maybe/no, one slot per person, a limit of people per slot, a password, signed-in users only, a required email, who sees the answers, hidden names, a closing date, and how long after closing to delete everything. Share the link or its QR code. People open it without an account, tap the times that suit them in a list or a week grid, in their own time zone or the poll's, and get a personal link to change their answers later. You see the answers arrive as a table, with a push notification for new ones, can export them as CSV, and settle on a slot: it becomes an event in the calendar you pick, encrypted if that calendar is, with optional invitations for people who answered with an account. The poll page then shows the decided time with an .ics file to add it to their own calendar. Polls need a server reachable from outside, so the Android app on its own and the desktop app in local mode don't offer them. An admin can switch them off in Settings, Admin. Database migration 0034 adds four tables for them. **Opens without the server.** In "connect to a server" mode, starting with no network or with the server down showed a blank screen on Android and the login screen in the browser. The app now opens from its last copy of your account and calendars, says it is connecting, and keeps retrying. **Fixed.** - Short events can be moved with the mouse again. Their resize bands covered the whole card. - The desktop app opens .ics files into a connected server too, and an address that does not answer is now an error in the chooser instead of a saved dead end. **License notices.** The web app, the Android app, the desktop app and the extension each ship a THIRD-PARTY-NOTICES.txt with the license texts of what they bundle, and builds refuse a dependency outside the license allowlist. A modified instance can point the About dialog at its own source with SOURCE_URL. **Store listing.** The Android and F-Droid description now leads with what the app does well and says it works on its own as well as with a server.
-
v2.1.7
protectedRelease: hyper-calendar 2.1.792ee8978 · ·hypercal 2.1.7 Typed times, search that ignores accents, readable text in every theme, and a long list of fixes found by a new round of property, mutation and browser tests. **Type a time.** With a mouse, the time panel has a text field above the hour and minute sliders. It takes "14:30", "2:30pm" or "1430", so any minute can be set, not just quarter hours. While focused it lists times in 15-minute steps, filtered by what was typed. It follows the 12-hour clock setting. Touch keeps the wheel. **Search ignores accents.** "munchen" finds "München", "strasse" finds "Straße", "soren" finds "Søren", and case is ignored in any script, not just A to Z. This holds for encrypted calendars, offline search and the Android app too. **Readable in every theme.** All text now meets WCAG AA contrast: faint labels, accent-coloured text, event titles on every event colour, and avatar initials. The accessibility check runs with no exceptions. **Open .ics files on the desktop.** hypercal appears under "Open With" for calendar files and can be their default app. A file opened while hypercal is running is handed to the running window. **Fixed on Android.** - Tapping an event opens its details and keeps them open. - Deleting an event worked, but was reported as failed. - Reminders match the server's: events from a phone calendar are no longer announced twice, and reminders set after the start are no longer dropped. **Fixed: dates and daylight saving.** - The week grid, the editor and "this and following" on the days clocks change, and "find a time" keeps working hours right on the day DST ends. - The Repeat preview shows the dates the server will store. - Search kept a repeating event out when its next date fell exactly on the end of the searched range. **Fixed: iCal.** - Events whose TZID is a bare UTC offset, such as REWE's delivery slots, import instead of being rejected. - Events in the hour a zone falls back keep their time through export and import. - A long rule with cancelled dates, a folded Outlook UID, and quotes or line breaks in text no longer change or break an event on its way through iCal. - CalDAV clients asking for events by hrefs with `&`, or in their own namespace, get them. **Fixed: everything else.** - Files added while editing one date of a repeating event are saved with "All events"; with a narrower scope the app says why they are not. - An offline edit followed by a delete no longer conflicts with itself, and a replaced offline conflict no longer returns after a reload. - A dropped .hypercalendar file imports again. - A 30-day reminder whose moment lands exactly on a tick is sent. - An empty `VAPID_SUBJECT` no longer turns off push notifications. - A second quick key press is no longer lost, and every switch in Settings has a name for screen readers. - Live updates stop when you sign out right after start. **Under the hood.** Property tests with fast-check across recurrence, iCal, offline sync and the Android backend; mutation testing with Stryker; the Android backend checked against the server's; browser specs for the feed, calendar settings, attachments, account and admin flows, with the mock API checked against the real response schemas; and one coverage report merging unit and browser tests. CI keeps Playwright traces, hunts flaky tests weekly, and gives lint the memory it needs.
-
v2.1.6
protectedRelease: hyper-calendar 2.1.68f6bd102 · ·hypercal 2.1.6 Repeat rules you can actually edit, a Firefox extension, and one Settings dialog. **Custom repeat rules.** The Repeat controls no longer rewrite a rule they cannot express. A rule such as "the 2nd and 4th Tuesday", often written by a CalDAV client, now opens in a Custom mode that edits it as controls and as raw RFC 5545 text side by side, with a live preview of the dates it makes. A series can take extra dates, and its cancelled dates are listed and can be restored, offline too. A malformed rule is refused with a message that says what is wrong, instead of being stored and silently producing nothing. **Firefox extension.** Firefox on the desktop cannot install the PWA, so there is now an extension: - today's and next week's agenda in the toolbar popup and the sidebar, with a countdown badge and a Join button for meeting links; - quick add from the popup, the sidebar or the address bar (`cal fri 14:00 standup`); - right-click an .ics link to import or subscribe to it, or selected text to open hypercal's new-event editor filled in from it, in English or German; - reminders as Firefox notifications. It talks to a new `/ext` API, authenticated with an app password. The API is off until an admin turns on Settings, Admin, Browser extension access. The `.xpi` attached to this release is unsigned: Firefox release and ESR refuse it, while LibreWolf, Developer Edition and Nightly install it once `xpinstall.signatures.required` is set to false. **One Settings dialog.** "Manage account" in the avatar menu is now Settings, with Account, Connectors (CalDAV, MCP and app passwords) and Tasks as tabs. The separate gear button is gone. The week, day and month titles leave out the year while it is the current one. **Copy by dragging.** Hold Ctrl when you drop an event in the week view to copy it rather than move it, as a drop between two windows already did. **Removed: voice control.** The microphone button, the voice setting and the Vosk speech model are gone, and the Android app no longer asks to keep the screen awake. **Fixed.** - A repeat rule that can never match, such as the 30th of February, arriving over CalDAV or MCP could stall every request that touched it. Expansion now stops at the end of the range it needs. - Exporting to .ics or syncing over CalDAV could rewrite a rule with positioned weekdays, such as the last Friday of the month, into a different one. The rule now goes out exactly as stored. **Under the hood.** The CI pipeline is split into one file per stage, and lint now enforces that the backend only imports what the Android build can run, and flags promises nobody awaits.
-
v2.1.5
protectedRelease: hyper-calendar 2.1.5ff16d3ae · ·hypercal 2.1.5 Weather for the rest of the world, faster recurring calendars, and new ways to install: a multi-arch Docker Hub image, a Flatpak repository and an Arch package. **Weather beyond Germany.** Pick a source in Settings. Open-Meteo and MET Norway cover the world, Bright Sky covers Germany including past weeks, and the National Weather Service covers the United States. All four are free and need no API key, and each says what it covers. Page to a week a source cannot reach and the lens says so, instead of showing an empty grid. **Faster with many repeating events.** Recurrence expansion does less work per query, and two new partial indexes cover recurring series and long-running events. They are added by a migration that runs on start, like every other. **Lighter container.** The health check now probes with curl instead of starting Node every interval, and runs every 60s instead of 30s. The image also caps Node's heap with `NODE_OPTIONS=--max-old-space-size=128`, so one instance cannot grow to take a small shared VPS with it. A large multi-user instance may want more: the daily .ics export and wide range queries build their whole result in memory. Setting `NODE_OPTIONS` in your compose file replaces the default. **New ways to install.** - Docker Hub: `docker pull larskreuz/hyper-calendar:2.1.5`, for linux/amd64 and linux/arm64, so it runs on a Raspberry Pi or an ARM VPS. The GitLab registry image is still published as before. - Flatpak: a signed repository at https://hyper-calendar-7b58b2.gitlab.io/flatpak/, installable in one command. See the Flatpak install page in the docs. - Arch: a `.pkg.tar.zst` you can install with `pacman -U`, plus the rendered PKGBUILD and .SRCINFO for the AUR, all attached to this release. **Fixed.** - Choosing Change color in an event's menu could close the whole menu. - The Android install page returned 404 when loaded directly. It now lives at /install-android, and the old /fdroid link redirects there. **Under the hood.** React 19, Vitest 5, ESLint 10, @simplewebauthn 14 (which includes the fix for GHSA-2g3p-m8c9-hhwh in the server's CRL handling), node-ical 0.27, and the Android toolchain on AGP 8.13. The published APK keeps the same signing layout, so F-Droid's reproducible build still matches.
-
v2.1.4
protectedRelease: hyper-calendar 2.1.480b420b0 · ·hypercal 2.1.4 A desktop app, an endpoint AI clients can drive the calendar through, and calendars you can publish as a feed. **A desktop app.** hypercal now ships as an Electron shell, in two modes. Local mode runs the portable backend in the app's own process, against a real SQLite file under your user-data directory, so the desktop build gets the whole feature set the Android one gives up: filesystem backups, database snapshots, and a /dav that another app on the same machine can actually reach. Remote mode runs no backend at all. It points the window at your own instance and runs that instance's frontend, which is the only arrangement in which passkeys can work, because WebAuthn compares origins and a loopback proxy would present the wrong one. Closing the window hides it to a panel indicator instead of quitting, so the backend, its pollers and the CalDAV listener stay up with the window put away. The packages are an AppImage, a tar.gz, a deb, an rpm, a Flatpak and an AUR PKGBUILD that installs the tarball with its launcher entry and icons. There is no auto-updater, on purpose. Every one of those formats is owned by a package manager, and a second thing replacing the binary would fight it. The app asks the releases API once, and if something newer exists it raises the same toast the web app uses, with a link to the notes. It fails silently: offline, proxied, timed out and already-current all mean say nothing. **An MCP endpoint.** /mcp lets an AI client read and edit the calendar over the Model Context Protocol, with twelve tools covering search, create, update, recurrence overrides, conflict checks and free-slot queries. It authenticates with the app-specific passwords that already exist for CalDAV. It is off by default, and it is an admin setting rather than an environment variable, so an admin can see the feature exists and switch it on without shell access to the deployment. The setting is read per request, so both directions take effect at once, including for a client already connected. A disabled endpoint answers 503 identically to a good token and a bad one, so it is not somewhere to test app passwords. It stays off until someone decides otherwise, because turning it on lets every app password already minted on the instance reach /mcp. **Published calendar feeds.** A calendar can be published at /feed/<token>.ics for anything that subscribes to a URL. Encryption is checked when you publish and again on every serve, so no future write path can leak a private calendar by forgetting a hook, and making a calendar private auto-unpublishes it rather than failing for a reason you would have to go fix elsewhere. The feed path is redacted in the request log, since a subscribed client refetches forever and would otherwise write a live credential into the log several times an hour. Agents cannot publish. The MCP tools can see and revoke a feed, but making a calendar world-readable is not a capability a bearer token handed to an AI client should carry. **Export one calendar.** /api/export.ics and /api/export.hypercalendar now take an optional calendar id. Both also filter trashed events, which they did not before, so exports and the daily backups built on them silently carried deleted events and disagreed with what the app showed. **Themes.** Three new palettes, Midnight, Nord and Paper, bringing the set to six. Follow system is new as well, and is a preference rather than a seventh palette: it picks the first palette matching your OS scheme, so the radius scale and type stack still resolve from a real theme. Underneath, four of the six are now derived from Radix Colors rather than values somebody picked, and a theme is one object in one file instead of six coordinated edits across an enum, a stylesheet, a picker, a label map and the pre-paint script. Blacksite and Nord are deliberately not derived, one being a port of a specific skin and the other a published palette in its own right. Importing a professional scale still needed measuring against our own surfaces, and the contrast test caught three places where Radix's guarantees do not carry across; the generator handles each of them rather than a hand-applied patch. **Dialogs, menus and tooltips.** The hand-rolled dialog, menu, select, popover and tooltip variants are one primitive each now, which is what makes tooltips work on touch. **The calendar list is rebuilt.** Encrypted, read-only, shared, published and CalDAV state are badges with visible text and an sr-only phrase, rather than glyphs behind a native title. Sync health renders for CalDAV links too: the server has always written lastFetchError for both mechanisms and only the UI distinguished them, so a failing link showed nothing at all. Sections group and collapse with counts. **Drag an event between windows.** Holding Alt arms a native drag on an event card, so it can be dropped into another window of the same browser, with a real ghost drawn while it is in flight. Fixes: - A write from a locked window could send an encrypted event's title, description and location to the server in the clear, and without the encrypted flag. It now refuses. A times-only write still passes through, so moving or resizing an event from a locked window keeps working. - Shutdown waited on the SSE stream the app holds open per client for change notifications, which is never idle, so server.close() hung. Not a desktop-only fix: under Docker the same hang meant waiting for SIGKILL on every deploy. - The desktop app could not be quit, and its first-run mode chooser was dead code. Both were turned up by the new Electron test suite. better-sqlite3 is on 13, which is N-API and so needs no rebuild against Electron's ABI. Android: versionCode 10. Feeds and /mcp are server features; the on-device backend reports 501 and 503 for them rather than pretending.
-
v2.1.3
protected9aa5e36d · ·hypercal 2.1.3 A desktop app, an endpoint AI clients can drive the calendar through, and calendars you can publish as a feed. **A desktop app.** hypercal now ships as an Electron shell, in two modes. Local mode runs the portable backend in the app's own process, against a real SQLite file under your user-data directory, so the desktop build gets the whole feature set the Android one gives up: filesystem backups, database snapshots, and a /dav that another app on the same machine can actually reach. Remote mode runs no backend at all. It points the window at your own instance and runs that instance's frontend, which is the only arrangement in which passkeys can work, because WebAuthn compares origins and a loopback proxy would present the wrong one. Closing the window hides it to a panel indicator instead of quitting, so the backend, its pollers and the CalDAV listener stay up with the window put away. The packages are an AppImage, a tar.gz, a deb, an rpm, a Flatpak and an AUR PKGBUILD that installs the tarball with its launcher entry and icons. There is no auto-updater, on purpose. Every one of those formats is owned by a package manager, and a second thing replacing the binary would fight it. The app asks the releases API once, and if something newer exists it raises the same toast the web app uses, with a link to the notes. It fails silently: offline, proxied, timed out and already-current all mean say nothing. **An MCP endpoint.** /mcp lets an AI client read and edit the calendar over the Model Context Protocol, with twelve tools covering search, create, update, recurrence overrides, conflict checks and free-slot queries. It authenticates with the app-specific passwords that already exist for CalDAV. It is off by default, and it is an admin setting rather than an environment variable, so an admin can see the feature exists and switch it on without shell access to the deployment. The setting is read per request, so both directions take effect at once, including for a client already connected. A disabled endpoint answers 503 identically to a good token and a bad one, so it is not somewhere to test app passwords. It stays off until someone decides otherwise, because turning it on lets every app password already minted on the instance reach /mcp. **Published calendar feeds.** A calendar can be published at /feed/<token>.ics for anything that subscribes to a URL. Encryption is checked when you publish and again on every serve, so no future write path can leak a private calendar by forgetting a hook, and making a calendar private auto-unpublishes it rather than failing for a reason you would have to go fix elsewhere. The feed path is redacted in the request log, since a subscribed client refetches forever and would otherwise write a live credential into the log several times an hour. Agents cannot publish. The MCP tools can see and revoke a feed, but making a calendar world-readable is not a capability a bearer token handed to an AI client should carry. **Export one calendar.** /api/export.ics and /api/export.hypercalendar now take an optional calendar id. Both also filter trashed events, which they did not before, so exports and the daily backups built on them silently carried deleted events and disagreed with what the app showed. **Themes.** Three new palettes, Midnight, Nord and Paper, bringing the set to six. Follow system is new as well, and is a preference rather than a seventh palette: it picks the first palette matching your OS scheme, so the radius scale and type stack still resolve from a real theme. Underneath, four of the six are now derived from Radix Colors rather than values somebody picked, and a theme is one object in one file instead of six coordinated edits across an enum, a stylesheet, a picker, a label map and the pre-paint script. Blacksite and Nord are deliberately not derived, one being a port of a specific skin and the other a published palette in its own right. Importing a professional scale still needed measuring against our own surfaces, and the contrast test caught three places where Radix's guarantees do not carry across; the generator handles each of them rather than a hand-applied patch. **Dialogs, menus and tooltips.** The hand-rolled dialog, menu, select, popover and tooltip variants are one primitive each now, which is what makes tooltips work on touch. **The calendar list is rebuilt.** Encrypted, read-only, shared, published and CalDAV state are badges with visible text and an sr-only phrase, rather than glyphs behind a native title. Sync health renders for CalDAV links too: the server has always written lastFetchError for both mechanisms and only the UI distinguished them, so a failing link showed nothing at all. Sections group and collapse with counts. **Drag an event between windows.** Holding Alt arms a native drag on an event card, so it can be dropped into another window of the same browser, with a real ghost drawn while it is in flight. Fixes: - A write from a locked window could send an encrypted event's title, description and location to the server in the clear, and without the encrypted flag. It now refuses. A times-only write still passes through, so moving or resizing an event from a locked window keeps working. - Shutdown waited on the SSE stream the app holds open per client for change notifications, which is never idle, so server.close() hung. Not a desktop-only fix: under Docker the same hang meant waiting for SIGKILL on every deploy. - The desktop app could not be quit, and its first-run mode chooser was dead code. Both were turned up by the new Electron test suite. better-sqlite3 is on 13, which is N-API and so needs no rebuild against Electron's ABI. Android: versionCode 9. Feeds and /mcp are server features; the on-device backend reports 501 and 503 for them rather than pretending.
-
v2.1.2
protectedRelease: hyper-calendar 2.1.297f23394 · ·v2.1.2 A weather lens for the week, and themes you can write yourself. The thermometer button in the toolbar lays an hourly forecast over the calendar: a temperature wash per hour, rain bars scaled by amount and faded by how likely the rain is, a high and low per day in the header, and DWD severe-weather warnings on a row of their own. The calendar recedes underneath while the lens is up and comes straight back when it closes. The day view is the same component with one column, so it gets the lens too. Nothing is fetched or drawn until you press the button, and the lens starts closed on every load. It is something you reach for, not a mode you leave the calendar in, so settings hold only the configuration: a location and a source. The forecast is fetched by the server rather than the page. The app's own CSP is connect-src 'self', so the page could not reach a weather host in any case, and this way your coordinates never leave your instance. Operators who would rather not depend on a public endpoint can point WEATHER_BRIGHTSKY_URL at their own Bright Sky. The data is DWD's, published under CC BY and GeoNutzV, and the credit rides on the grid while the lens is up. Alongside it, two changes to theming. Blacksite is a third built-in theme: carbon ground, one red accent, squared corners and monospace type, with destructive actions pushed off the accent so Delete still reads as Delete. And there is now a Custom theme, which is a layer rather than a blank page. Pick which built-in theme to start from, override individual colour tokens, and add up to 16 KiB of your own CSS on top. It is stored on your account rather than the device, so it follows you everywhere you sign in. If a stylesheet ever leaves the app unusable, open it with ?safemode=1 to load without it; the editor still works, so you can fix the rule or reset. Android: versionCode 8.
-
v2.1.1
protectedRelease: hyper-calendar 2.1.128671a13 · ·v2.1.1 The APK no longer carries Google's encrypted dependency-metadata block. Android's build tools embed that blob into every signed APK by default: the app's dependency list, encrypted with a Google public key and readable only by Google. This app is not distributed through Google Play, so it was never anything but dead weight, and F-Droid refuses an APK that carries one. Removing it is what lets the official F-Droid build ship this exact signed APK instead of a differently signed rebuild. F-Droid builds the app from source on its own infrastructure, compares the result against this release byte for byte, and distributes this file when the two match. The GitLab release, the project's own F-Droid repository and the official F-Droid repository are therefore one signing key, and moving between them is an ordinary upgrade rather than an uninstall that would take your calendar with it. No functional changes.
-
v2.1.0
protectedRelease: hyper-calendar 2.1.01c0da59d · ·hypercal 2.1.0 A new name, a from-source build, and a server that is markedly harder to attack. **`WEBAUTHN_ORIGIN` is now required in production.** A server started with `NODE_ENV=production` and no `WEBAUTHN_ORIGIN` refuses to boot. Set it to the public origin people reach the instance on, scheme included — e.g. `https://calendar.example.com`. It is not a convenience setting: without it a passkey assertion is checked against the caller's own `Origin` header, which is a check that can only pass. `WEBAUTHN_RP_ID` remains optional and defaults to that origin's hostname. `AUTH_DEV_AUTOLOGIN=1` together with `NODE_ENV=production` is now fatal rather than a warning — it treats every session-less request as a seeded admin, which is a complete authentication bypass. The Playwright suites run as the new `NODE_ENV=e2e`, which is production behaviour in every respect that matters and tolerates the bypass the harness needs. Nothing else needs attention. The schema migrates itself and the Android package identifiers are unchanged, so this is an ordinary in-place update. The name is shorter everywhere a person reads it — launcher, PWA manifest, browser tab, F-Droid listing — with a new icon and a refreshed set of screenshots. `dev.hypercalendar.app` is untouched, and so is the CalDAV realm string: clients cache credentials against (host, realm), so renaming it would make every configured device re-prompt for its app password in exchange for a name nobody ever sees. The SQLite engine the on-device backend runs on is now compiled from source during the build — emscripten, in CI — instead of arriving as the prebuilt `.wasm` inside `@sqlite.org/sqlite-wasm`. F-Droid builds every APK from source on their own infrastructure and their scanner rejects a prebuilt binary outright, so this is what makes a from-source build possible at all. Doing it in our pipeline too, rather than only inside the F-Droid recipe, is the point: the published APK and the one F-Droid builds now run the same engine, and the standalone test suite exercises it on every tag instead of leaving it a path only their reviewer discovers is broken. - **Per-username login throttling.** The existing per-IP limiter cannot help with the case that actually matters for a password: many hosts making a few attempts each against one account, which stays under every per-IP ceiling indefinitely. Failures are now counted against the account itself, wherever they come from. It is a throttle and not a lockout — each failure past a small free allowance imposes a doubling delay, so knowing a family member's username is not a way to lock them out of their own calendar. Failed CalDAV Basic auth feeds the same counter. - **Origin checking on state-changing `/api` requests.** `SameSite=Lax` cookies already stop a cross-site POST, but Lax treats sibling subdomains as same-site, so an untrusted neighbour under the same registrable domain was not covered. Requests carrying an `Origin` must now match this instance's; requests with no `Origin` are unaffected, because they did not come from a browser being tricked. `CSRF_ALLOWED_ORIGINS` adds deliberate extras. - **Passkey sign-in no longer skips 2FA when it proves only possession.** User verification is requested as "preferred", so an authenticator that performs none still returns a valid assertion. For an account that has deliberately enabled TOTP, that assertion now falls through to the same second step the password flow uses. - **A real Content-Security-Policy for the SPA shell.** The one inline script — the pre-paint theme read — is allowed by a hash computed at startup from the file the server is about to serve, rather than by `'unsafe-inline'` or by a constant that drifts the next time someone edits the shell. - **SSRF hardening** on the outbound fetches behind ICS subscriptions and remote CalDAV links, with the same guard shimmed into the on-device backend. - **Signing out wipes the device.** The offline event cache, the pending write queue, the conflict list and the service worker's cached `/api/events` responses are all cleared, so the next account on a shared device starts clean. - **A leaner, pinned container.** Base images are pinned by digest, and the native build toolchain `better-sqlite3` needs is confined to a build stage instead of shipping to production. `docker-compose.yml` is now a hardened reference deployment — read-only root filesystem, no new privileges. The security model, security policy, deployment and self-hosting pages have all been reworked around the above, including what the optional per-calendar encryption does and does not defend against.
-
-
v2.0.2
protectedRelease: hyper-calendar 2.0.29e1f2289 · ·hyper-calendar 2.0.2 The app now has a name, a face, and a way to talk back. Android and F-Droid * The app is called **Hyper-Calendar** everywhere a person reads it — launcher, PWA manifest, browser tab and the F-Droid listing. Package identifiers are unchanged, so this is an ordinary in-place update. * A real launcher icon. Every density shipped Capacitor's scaffolding artwork until now, as did all eleven splash screens. The mark is rendered from SVG sources in assets/ via 'npm run icons'. * Reminder notifications get their own status-bar icon. capacitor.config.ts had always named an ic_stat_icon resource that did not exist, so they fell back to a generic system glyph. The F-Droid listing shows images at last 'fdroid update' reads per-app images from fdroid/repo/<applicationId>/<locale>/, and nothing ever put them there — so the app page had no screenshots, no banner, and only whatever icon could be scraped from the APK. It now carries an icon, a feature graphic and four screenshots, generated from the running app by 'npm run screenshots'. About and feedback, in the app * Account menu -> About: version, build, deployment mode, licence and links. Until now there was nowhere in the app to read the version the issue tracker keeps asking reporters for. * Account menu -> Send feedback: describe what happened and it composes a report with the version, deployment mode, platform, browser, locale, timezone and window size already filled in, then opens a prefilled GitLab issue. Nothing is sent by the app — the diagnostics are shown and editable first, and the issue is public. Documentation The site is the single source of truth: the repository's markdown is now short pointers to it, and the site itself finally shows the product. This is the first release whose APK is built and signed by CI rather than uploaded by hand. Getting there took two fixes to that job: it died of SIGPIPE after accepting the SDK licences, and then needed a JDK 21 image, which Capacitor 8 requires and Debian 12 cannot provide.
-
v2.0.1
protectedRelease: hyper-calendar 2.0.1130ef4f2 · ·hyper-calendar 2.0.1 Corrected reverse-proxy documentation; APK re-signed with a new release key (CN=hyper-calendar). Not upgradeable in place from 2.0.0.
-
v2.0.0
protectedRelease: hyper-calendar 2.0.0cbff2609 · ·hyper-calendar 2.0.0 First published release. Renamed from weekcal. Docker image: registry.gitlab.com/kreuz-com-group/hyper-calendar:2.0.0 APK: dev.hypercalendar.app versionCode 2