Release notes
Unreleased
0.1.2 — Notification and code scanning fixes (2026-10-06)
- Digest notifications: two workers running at the same moment could split one owner's pending changes into two summary messages. Claims are now serialized per owner and channel, so each run sends a single digest.
- The stored ČÚZK API key fingerprint (used only to detect key rotation) is now derived with scrypt instead of plain SHA-256. After upgrading, the instance treats the key as rotated once: the cached ČÚZK account info and any temporary API pause are reset; the daily call budget is kept.
- The inline theme script in
<head>is a static literal (no interpolated values).
0.1.1 — Security update (2026-10-06)
- Raise transitive dependency floors flagged by Dependabot:
seroval≥ 1.6.8 (pulled in by Better Auth's optional Solid integration; the app does not call the affected deserialisation path, but the package shipped in the image),source-map-js≥ 1.2.2 andesbuild≥ 0.25 for development tooling. - Website build uses Vite 6.4 instead of Vite 5 (development-server advisories only; the published static site was not affected).
- Documentation explains how to obtain the ČÚZK API key (registration, terms of use, expiry and rotation).
0.1.0 — First public release (2026-10-06)
First open-source release of Hlídač ČÚZK: a self-hosted watcher for parcels, buildings, units, rights of superficies and proceedings in the Czech cadastre (ČÚZK) with change history and notifications. Install with Docker Compose following the self-hosting guide (CZ) or self-hosting guide (EN).
Project website with documentation: https://petrleocompel.github.io/hlidac-cuzk/
Dependencies refreshed: Better Auth 1.7 (account unlinking now uses the account id), Sentry SDK 11 (still initialised only with
SENTRY_DSN,sendDefaultPii: false), ESLint 10, jsdom 30, Vite 8.3 and current TanStack Start/Router.Published as open source under AGPL-3.0-only on GitHub; images are built by GitHub Actions and published multiarch to
ghcr.io/petrleocompel/hlidac-cuzkwith provenance and SBOM attestations.Opt-in bounded retention for events, historical snapshots, errors and API attempt detail; undelivered messages protect their source history, and daily quota totals are retained. Hourly maintenance reports counts; the manual CLI defaults to a dry run.
Polling pages due watches in batches of 100 with stable cursors and shares fetched data across owners/pages for every supported register. Migration
0016adds matching indexes.Private watch tags and notes, accent-insensitive portfolio search and KU/LV/status/tag filters. Atomic bulk pause, resume and interval updates validate ownership of every selected item; captured history and queued notifications survive pausing. Migration
0015is required.Bounded map-area preview (1 × 1 km, up to 20 parcel geometries) with individual confirmations.
Optional cadastral map on watch details and the overview, with orthophoto, stored definition points and exact parcel geometry from INSPIRE CP. Click or keyboard center selection offers a parcel for explicit verification and daily subscription. Map failure preserves text details.
Runtime image and development move to Node.js 26 (LTS from 28 October 2026); Corepack is installed from npm because Node 25+ no longer bundles it.
Watches can be added by address: RÚIAN suggests address places, the exact match yields the address place code and ČÚZK turns it into the building, which is offered together with its units and parcels. An ambiguous or unmatched address is confirmed by the user; coordinates and the geocoder's internal key are never used as identification.
Buildings, units and rights of superficies can be watched as objects of their own, next to parcels. Each register has its own attributes, snapshot and diff, and only the data the given API returns is stored. Buildings and units are searched by RÚIAN část obce, number type and house/unit number; rights of superficies are added by the ISKN id a related object shows. A parcel, building or unit detail offers its linked objects as separate watches.
Existing parcel watches are unchanged; uniqueness is now per user, register and ISKN id.
New Přehled podle LV groups the objects a user already watches by katastrální území and LV number with the latest ten events per group. It queries no ČÚZK data, says plainly that it is not the complete contents of an LV, discovers no property on its own and does not call an LV change a confirmed change of owner.
A polling cycle fetches each parcel once and shares the answer between all subscriptions of the same object; histories, labels, intervals and channel rules stay per user.
Neighbor discovery previews up to 100 basic parcel definitions and atomically adds up to 20 selected daily watches with quota estimates, ownership checks and duplicate protection.
Per-watch notification filters, quiet hours with timezone, daily/weekly digests and urgent-event bypass. Durable digest batches retain failed events and recover after crashes.
SMTP e-mail and self-hosted ntfy, independently controlled by administrators. Explicit opt-in enables shared instance Gotify; complete personal settings take precedence.
Notification links open the exact owner-only event, including older history pages.
A řízení is now followed as its own object: state changes, new completed operations and payment state are reported for the same řízení id, and its history survives the plomba disappearing from the parcel. After a plomba is removed the detail is still queried for
CUZK_RIZENI_FOLLOW_DAYS(default 14, at most ten follows per watch), so the actual result is reported instead of implying an approved vklad. A missing detail keeps the last known values; a 404 or empty payload ends the follow-up as confirmed unavailable.Users can add a known řízení (type, number, year, office code) or a linked
navazanaRizeniand stop the extra queries at any time.History now records the value before and after each change (
Výměra: 976 → 980 m²) plus the snapshot behind it, compares BPEJ and protections in a normalized order, and treats a list the API did not return as unknown rather than removed. The watch detail pages and filters the history and exports CSV/JSON with acquisition times; it also states that no history exists from before the watch was created.Adding a watch no longer needs the KÚ code: search by name without diacritics or by code, write the parcel as one field (
1133/77,st. 25) and confirm the verified ČÚZK answer before saving. Identification is taken from that answer, and the demo parcel is prefilled only withSEED_DEMO_WATCH=1.New CSV/JSON bulk import with a preview that spends no ČÚZK call, per-row errors with line numbers, duplicate detection and one verification call per imported row.
One user can watch one object only once; the upgrade reduces existing duplicates to the row with the most recorded events before adding the unique index. All histories, pending deliveries and distinct tracked procedures are transferred before removing duplicates.
AMD64 and ARM64 images must pass a real Compose installation smoke test before publishing.
Web, worker and migrations run as UID 1000 with read-only storage; runtime dependencies are installed separately and pnpm is preloaded for startup without a registry connection.
Loopback/LAN, Caddy TLS and standalone external PostgreSQL configurations are documented in Czech. Fonts are served locally; no DSN means no Sentry initialization.
Image publishing now requires successful lint, typecheck, tests and production build.
deploy/upgrade.shstops writers, creates a protected local PostgreSQL dump, runs one bootstrap and waits for web readiness before starting the worker.The administration monitoring page reports image version, full commit and schema state.
Production requests and workers no longer run implicit migrations. Start with
pnpm bootstrap.Readiness refuses a database with newer migration metadata than the running image.
Upgrading pre-release installations (schema 0008)
Installations created from pre-release builds at schema 0008 are upgraded in place. The upgrade adds 0009_backup_status, 0010_watch_rizeni, 0011_event_history, 0012_watch_uniqueness (which consolidates duplicate watches while preserving their histories and deliveries), 0013_notification_rules and 0014_object_watches (watched register plus uniqueness per user, register and ISKN id). Existing accounts, watches, events and pending notification deliveries are preserved. Back up the database and configuration/keys first; use the pinned image and the upgrade procedure. The regression test upgrades the previous schema and checks both preserved data and rejected startup after a migration failure. There is no promise of compatibility with arbitrary older images.
Earlier selfhosting changes require a persistent NOTIFICATION_ENCRYPTION_KEY and explicit pnpm notifications:encrypt for old plaintext credentials. Registration defaults to private. Scheduled off-site backups remain optional until their target and recovery credentials are configured. An unreachable SSO provider on a redeploy produces a bootstrap warning; verify SSO independently after deployment.