Tags

Tags give the ability to mark specific points in history as being important
  • v2.2.4

    protected Release: hyper-calendar 2.2.4
    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

    protected Release: hyper-calendar 2.2.3
    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

    protected Release: hyper-calendar 2.2.2
    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

    protected Release: hyper-calendar 2.2.1
    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

    protected Release: hyper-calendar 2.2.0
    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

    protected Release: hyper-calendar 2.1.7
    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

    protected Release: hyper-calendar 2.1.6
    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

    protected Release: hyper-calendar 2.1.5
    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

    protected Release: hyper-calendar 2.1.4
    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

    protected
    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

    protected Release: hyper-calendar 2.1.2
    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

    protected Release: hyper-calendar 2.1.1
    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

    protected Release: hyper-calendar 2.1.0
    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.3

    protected Release: hyper-calendar 2.0.3
  • v2.0.2

    protected Release: hyper-calendar 2.0.2
    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

    protected
    130ef4f2 · Release 2.0.1 ·
    Release: hyper-calendar 2.0.1
    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

    protected Release: hyper-calendar 2.0.0
    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