katastar
Croatian cadastre AI platform. AI-driven access to land and property records.
What it is
Conversational access to the Croatian cadastre — the public-but-painful land and property registry split across municipal offices and an antique central system. Ask in plain Croatian (or English): “What’s the ownership history of this parcel?”, “Show all parcels in Šibenik over 1000 m².” Get an answer with citations back to the underlying records.
The problem
Cadastre data exists, but the access patterns are hostile. Municipal websites publish in idiosyncratic formats, the central registry has connection drops mid-response, and the data itself is dense legal Croatian that lay users can’t parse. There was no single tool that combined retrieval, normalization, and natural-language querying — so I built one.
The approach
A two-app monorepo: a Go API that ingests, normalizes, and serves the data, and a React + Vite + Tailwind frontend with a chat interface, raw-SQL panel, and admin ingestion controls.
- Ingestion — scheduled scrapers + an admin panel for triggering municipality re-runs. Each fetch retries with exponential backoff and accepts cancellation contexts so a flaky gov’t connection doesn’t kill the run.
- Storage — DuckDB on a bind-mount volume. Embedded analytics DB, no server, perfect for “the data is large but doesn’t change often” workloads.
- Chat — assistant-ui on the React side, talking to a Go-side agent loop that calls SQL tools against DuckDB. The agent shows its work — every claim links back to the rows it queried.
- Auth — JWT + PBKDF2, single shared password gate. Multi-user is on the roadmap.
Tech notes
Deployed on the kulify Coolify VPS as two apps from one repo (separate Dockerfiles, separate domains for API and web). The deployment recipe — base directory at root, Dockerfile location per app, DuckDB locking quirks under rolling updates — is what got me into the deep end of Coolify’s CLI gotchas, captured as a reusable pattern across the kulify ecosystem.
The frontend started life as a Lovable export, then got a real refactor: TanStack Query for server state, Zustand for UI state, a proper admin tab.