Skip to Content
⚠️Active Development Notice: TimeTiles is under active development. Information may be placeholder content or not up-to-date.
ReferenceChangelog

Changelog

All notable changes to TimeTiles are documented here. Format based on Keep a Changelog .

See the release process for how new versions are cut.

[Unreleased]

Changes landed on main since the last tagged release.

[0.1.0-alpha.28] - 2026-11-09

Fixed

  • Job workers: a long job holds one slot instead of blocking its whole queue until it ends
  • Scraper setup: bootstrap step 13 installs the runner of the release in .env.production, not an older one from the bootstrap config

[0.1.0-alpha.27] - 2026-11-09

Fixed

  • Scheduled ingests: a finished run no longer resets the next run time to an old value, which made every schedule fire again each minute

[0.1.0-alpha.26] - 2026-11-08

Fixed

  • Background jobs: every task reports a heartbeat, and timetiles ingest recover --apply closes a scheduled task whose worker died, so its schedule resumes

[0.1.0-alpha.25] - 2026-11-08

Added

  • Ingest: each sheet runs as its own job within a recorded run, the event quota is charged through a ledger, so retries and crashes cannot charge twice, and timetiles ingest recover reports work whose worker died and resolves it with --apply
  • Layout templates: site default and per-page header and footer frames

Changed

  • Production refuses to start without EMAIL_SMTP_HOST unless EMAIL_DISABLED=true is set; SMTP requires TLS by default
  • Workers run scripts/jobs-worker.ts and finish the batch in flight on shutdown
  • Login, registration and account forms carry the autocomplete attributes password managers rely on

Fixed

  • Ingest: every pipeline stage is safe to retry; geocoding keeps its results between attempts
  • Import wizard: a stale edit session no longer takes over a new import, and the next sheet’s review starts with a fresh decision
  • Account deletion removes trashed rows too and deletes upload files only after the transaction commits
  • All-in-One: the documented docker run command sets the required mail option

Security

  • TimeScrape: git clones connect only to the addresses the SSRF guard vetted and cap the transferred bytes
  • Only editors and admins can write media; REST requests check their ingest targets

[0.1.0-alpha.24] - 2026-11-07

Changed

  • Linting runs on oxlint alone; the remaining ESLint plugins run as oxlint JS plugins, and layer boundaries are now also checked for @/ imports
  • Minor and patch dependency updates, tsup 9; Dependabot skips Payload, which is upgraded as one unit

Fixed

  • Explore: a click on the map/list switch is no longer lost when the map writes its position at the same moment; the destination opens where the map came to rest
  • Footer funding logos render through next/image, and the global error page reloads home without the router
  • Request timing padding no longer swallows falsy rejections
  • DNS lookups for URL validation and the TimeScrape SSRF guard use order: "verbatim" instead of the deprecated flag
  • TimeScrape: the node scraper image installs its build tools at the versions locked in pnpm-lock.yaml, so tool updates no longer break the image build

[0.1.0-alpha.23] - 2026-10-30

Fixed

  • Deployment: Podman copies images through a directory inside the app user’s image store, so pulls and builds also work once the runner is up, whose unit keeps /var/tmp read-only

[0.1.0-alpha.22] - 2026-10-30

Fixed

  • TimeScrape: the runner addresses its scraper images as localhost/timescrape-<runtime>; resolving the short name needs a lock the runner unit keeps read-only, so after a reboot no image was found

[0.1.0-alpha.21] - 2026-10-30

Fixed

  • Deployment: the scraper setup resets a Podman store that recorded /tmp as its run root, which the runner unit cannot write, so the runner finds its scraper images again instead of reporting them unavailable
  • TimeScrape: the runner logs the Podman error behind a failed image or network check

[0.1.0-alpha.20] - 2026-10-30

Changed

  • Explore: tag filters use the GIN index on events.transformed_data, with a migration that drops the duplicate index on that column
  • Quotas: an admin can approve a quota-exceeded import only once the user’s limit allows it; until then the approval answers 409
  • Minor updates of the lint tooling, with patched audited advisories (handlebars)

Fixed

  • Deployment: the TimeScrape runner starts its scraper containers again after a host reboot; its unit no longer sets NoNewPrivileges, which kept rootless podman from mapping its user namespace
  • Map: explains when the map cannot start instead of loading forever
  • Mobile: the navigation drawer shows account links and sign-out, and the import wizard keeps its back and edit-mode labels on small screens
  • Forms: values entered before hydration are kept
  • Explore: the event list keeps its live-announced description of counts and filters when nothing matches
  • Imports: a source with only a header row completes as a run with nothing to import, and sheets are routed by the workbook’s sheet count rather than by the sheets that contain rows
  • Event detail: a mapped description path is authoritative

[0.1.0-alpha.19] - 2026-10-26

Changed

  • Payload CMS 3.92.0, with a migration that indexes the localized content tables
  • API: requesting a document’s draft by ID (?draft=true) without access to that draft answers “not found” instead of returning the published version
  • @timetiles/ui: DataTable builds on TanStack Table 9, and DataTable and DataTableProps take the row type as their only type parameter
  • Minor and patch updates of the remaining dependencies, including MapLibre GL 6.16

Fixed

  • Docs: the documentation site builds again with zod 4.4 and later
  • Imports: files with several sheets no longer fail sporadically while creating schema versions in parallel
  • Quotas: concurrent creates at a reached limit answer “limit reached” instead of stalling the database connection pool
  • Scheduled imports: saving a schedule applies its schema configuration to several datasets one after another instead of concurrently on one transaction
  • Development: the e-mail fallback replaces an expired ethereal.email account instead of failing with “Invalid login: 535”

[0.1.0-alpha.18] - 2026-10-26

Changed

  • Payload CMS 3.90.2 and Next.js 16.4.3

Fixed

  • Explore: focusing on a cluster cell shows the clusters inside that cell instead of the whole viewport
  • Import: large uploads arrive whole, and multipart uploads are capped at 100 MiB to match the file-size quota
  • Deployment: the web container gets an executable cache mount for the SWC native addon, which the noexec /tmp could not load

Security

  • Upgraded dependencies with known vulnerabilities: Next.js, nodemailer, undici, zod, DOMPurify, happy-dom, esbuild, simple-git and brace-expansion

[0.1.0-alpha.17] - 2026-09-29

Fixed

  • Import: CSV preview, schema detection, row count and import read a file the same way — a leading byte order mark, blank lines before the header, duplicate or padded header names and delimited .txt files no longer make them disagree
  • Import: valid UTF-8 files are no longer misread as a single-byte charset, so Köln stays Köln
  • Import: counting rows of a missing sheet or an unsupported file type fails instead of reporting zero, and duplicates written in place under the update strategy are no longer reported as skipped
  • Login: overlapping logins of one account no longer drop each other’s session, and expired sessions are removed on login
  • Explore: a zoom or pan made before the map style loads is kept, and a map view from the URL is no longer replaced by the fit to the data
  • Explore: the map reports itself busy until its clusters match the view, and the theme picker no longer shifts the header while the page loads
  • API: /api/v1/sources/stats counts events of public datasets whose catalog was deleted in totalEvents
  • API: /api/legal-notices and dataset schema inference report failures instead of answering with empty notices or “not found”
  • Development: validate-types checks Payload’s generated files without rewriting them or leaving backups

Security

  • A catalog made public, or a dataset moved to a public catalog, only in a draft no longer makes its events public before the change is published
  • Approving an import job no longer applies field-mapping changes to a dataset the approver cannot edit

[0.1.0-alpha.16] - 2026-09-28

Upgrade note: the scraper runner no longer defaults its work area to /tmp/timescrape and refuses to start without an absolute SCRAPER_DATA_DIR. Bootstrap step 13 writes it into the runner’s systemd unit. timetiles update leaves a runner whose unit predates this in place and names the step to rerun: sudo deployment/bootstrap/bootstrap.sh --step 13-scraper-setup.

Changed

  • Cache: the general cache keeps only its filesystem backend; the cleanup job fails, and is retried, when cleanup or statistics fail instead of reporting invented numbers
  • Scrapers: the reported row count skips a record holding one empty field, as the import does

Fixed

  • Scrapers: CSV rows are counted per record, so quoted fields spanning lines or containing literal quotes no longer skew the count
  • Scrapers: the Node and Python SDKs refuse rows without fields instead of writing an empty file
  • Scrapers: runs whose output the watchdog cannot read are killed, and the reason names the path
  • Scrapers: output download and cleanup errors are reported instead of answering 404 or staying silent
  • Scrapers: a clone whose repository size cannot be read fails instead of passing the size limit
  • Scrapers: the run’s stderr names the signal that ended podman
  • Scrapers: the runner rejects empty or out-of-range settings at startup, and init rejects unknown arguments
  • CLI and bootstrap: the runner is swapped by renames, so an old install that cannot be deleted is still replaced as a whole
  • CLI and bootstrap: .env lines with CRLF endings are read without the carriage return

Security

  • Scrapers: repository clones refuse a git host the SSRF check cannot resolve

[0.1.0-alpha.15] - 2026-09-28

Fixed

  • CLI: update finishes in a fresh process after pulling the deployment files, also with --no-self-update, so the rest of the update runs the pulled CLI with the pulled libraries

[0.1.0-alpha.14] - 2026-09-28

Changed

  • Ingest: uploads accept one shared MIME allow-list; the Google Sheets type is gone because no parser reads it
  • CLI and bootstrap: runner install, podman calls and .env reads share one implementation; bootstrap replaces the runner atomically, so a failed install keeps the old one
  • CLI and bootstrap: .env values containing = or quotes are read whole; a repeated key takes its last assignment

Fixed

  • Scrapers: an invalid cron expression disables the scraper and marks the run failed instead of skipping it silently forever
  • Ingest and seeding: lookup errors other than “not found” are raised instead of treated as a missing record
  • CLI: a failed image pull shows the registry error before falling back to a local build; a failing alert script is reported as a warning

Security

  • URL fetches and scraper repo sync refuse the request when the SSRF DNS check cannot resolve the host, instead of skipping the check

[0.1.0-alpha.13] - 2026-09-28

Fixed

  • CLI: a root invocation keeps TIMETILES_DISCARD_CONTAINER_DB when it hands over to the deploy user, so the “Database Outside the Volume” reset works under sudo
  • CLI: create-admin without a terminal fails with a message naming TIMETILES_ADMIN_PASSWORD instead of exiting silently
  • File cache: destroy() waits for the index write and reports its errors instead of dropping them
  • CMS blocks: list items are keyed by their Payload id

Security

  • Ingest: transform expressions use expr-eval-fork, which rejects prototype access such as constructor and __proto__ (CVE-2025-12735, CVE-2025-13204)

[0.1.0-alpha.12] - 2026-09-28

Fixed

  • Bootstrap: steps run from /, so podman invoked as the app user no longer aborts on an inherited working directory it cannot enter
  • Bootstrap: the scraper-setup step restarts the runner, so a rerun applies the rewritten systemd unit instead of leaving the old one active

[0.1.0-alpha.11] - 2026-09-28

Republishes alpha.10: its image release stopped at the deployment test, so no alpha.10 images were published. The upgrade note from alpha.10 applies.

Fixed

  • Release pipeline: the deployment test environment gives the log and export bind mounts to the container user, as bootstrap does, so the containers no longer crash-loop on unwritable log files

[0.1.0-alpha.10] - 2026-09-28

A large hardening release covering deployment, background jobs, ingest, the URL cache and access control. Production compose now runs one worker per job queue, and only the web container runs migrations. Imports into the same dataset now run one at a time. Upgrade note: the postgres cluster now lives in the postgres_data volume (kartoza DATADIR). On installs created before this release, the database sits in the container’s writable layer, and timetiles up/down/restart/update refuse to run. Take a backup first, then follow “Database Outside the Volume” in deployment/README.md.

Added

  • timetiles create-admin <email> creates the first admin on a fresh install. It takes the password from the environment, skips email verification, and refuses to change an existing account. Bootstrap now prints next steps that point to it
  • New deployment settings: INGEST_FILE_RETENTION_HOURS / INGEST_FILE_ORPHAN_GRACE_HOURS (import working-file cleanup), TRUSTED_PROXY_CIDRS (client IP for rate limiting behind a proxy), DOCKER_NETWORK_SUBNET (pinned bridge subnet that the scraper-runner firewall rule relies on) and EXPORT_HOST_DIR (shared data-export directory)
  • Scrapers: owner-scoped repository sync status, with sync errors shown in the UI
  • Development: make setup-mac provisions a macOS machine (including the h3 extension), and PG_LOCAL_PORT sets the local PostgreSQL port

Changed

  • Database: the postgres data directory is now /var/lib/postgresql/data inside the postgres_data volume. Lifecycle commands guard against installs whose database is outside the volume, and TIMETILES_DISCARD_CONTAINER_DB=1 overrides the guard
  • Version history is capped at 10 versions per event and 5 per ingest job. Media no longer keeps drafts or versions
  • Imports: imports into one dataset are serialized with a Postgres advisory-lock lease, while different datasets still import in parallel. A failed update-strategy import rolls back fully, including sheet imports
  • Deployment: worker-general, worker-ingest and worker-maintenance each consume their own queue, and exactly one of them handles schedules. The workers wait for web to finish migrating. The all-in-one image now runs background jobs and logs nginx to stdout. Updates pull deployment files from the pinned release tag
  • The map uses maplibre-gl v6 with its worker served from the app, so the CSP no longer allows blob workers
  • Ingest-created catalogs, datasets and events are published on creation. Draft catalogs, datasets, events, footers and menus are hidden from public and non-owner reads
  • Generated slugs transliterate umlauts and accents, and stored slugs no longer change on save. Feature flags apply as soon as settings are saved
  • Accessibility and i18n: map control popovers are keyboard accessible, closed filter surfaces leave the tab order, result changes and form errors are announced, the date slider moves by whole days, and dates, chart labels and remaining UI strings follow the app locale (EN/DE)

Removed

  • The HOST_PORT and CONTAINER_PORT deployment variables. The app port is fixed at 3000, and changing CONTAINER_PORT used to deadlock the stack
  • The placeholder EMAIL_SMTP_HOST in the env template. Leave it unset to run without outgoing mail
  • npm from the production runtime image, and ineffective schedule-retry and job-timeout settings

Security

  • Access control: closed IDOR holes, XSS sinks, fail-open query and H3 filters, and a catalog-delete grant leak. Clients can no longer write credential, derived-access, pipeline, scraper run-state or scheduler fields. Native job controls and user creation are restricted to admins, and ingest-job updates to editors and admins
  • Auth: password policy enforced and sessions revoked on reset, credentials revoked on deactivation, cancel-deletion attempts rate-limited, and rate-limit keys namespaced with the client IP taken only from trusted proxies
  • SSRF and redirects: closed the IPv4-mapped IPv6 bypass and redirect targets that smuggle a host past the prefix check. Bracketed IPv6 clone hosts are classified correctly
  • Data leaks: user data is escaped in emails, 5xx responses and export emails no longer expose internal errors, logs no longer contain URLs, OAuth, cache or provider details, or subscriber emails, and persisted cache keys hash their source URLs. Exports no longer strand personal data, and account deletion serializes with export publication
  • CSV downloads are formula-escaped at the download boundary, including handling for delimiters, BOM, sep= and SYLK. Stored ingest data is left unchanged
  • Scraper sandbox: runner containers are bounded, rootless Podman egress is fenced by the app user’s uid, the seccomp profile ships, and run output is read without following symlinks

Fixed

  • Deployment and backup: four containers no longer migrate one database at once, and quoting /etc/timetiles.env protects passwords with spaces or backticks. A failed runner is detected and kept on a bad update, and a failed offsite backup alerts. restore latest picks the newest snapshots and swaps uploads in atomically. Also fixed: a certbot anonymous-volume leak, and nginx static asset locations that dropped security headers
  • Ingest and schema: source encoding is detected and phantom blank rows are dropped. EU decimals, timed dates and multiline CSV headers parse correctly. Excel previews match imported column names. Pagination limits and OAuth, token and custom-header auth configs are validated. Cron and DST schedule edge cases (nonexistent midnight, leap days, empty timezones) are handled. Retries and manual triggers are queued in the claim transaction. Stale mappings are cleared, and data-package activation parameters are validated
  • Jobs and cleanup: stuck-ingest wedging, quota rollback and an audit transaction leak are fixed. Partial cleanup failures (exports, previews, ingest files, audit IPs, schema maintenance) are retried. Processing and restarted jobs are kept out of cleanup. A scraper run that never started refunds its quota
  • Scrapers: run timeouts use kill escalation and keep partial logs. Leftover work directories are swept. Deleting a scraper while a run is active is refused, and a duplicate active run id returns 409. Schedules stop after deactivation, and cron syntax is validated before saving
  • URL cache: honors Vary, quoted and mixed-case cache-control, zero TTL and mandatory revalidation. Partial responses are kept out of the cache, the persisted index is validated, and cleanup runs in the ingest worker
  • Geocoding: provider calls abort on timeout, backoff and throttles are respected, disabled fallback and provider groups are honored in batches, Photon gateway failures are retried, and the kill switch no longer fails open when settings cannot be read
  • Explore, charts and events: histogram buckets cover ranges beyond 68 years, and a UTC session allows pre-1900 timestamps. Trashed events are hidden from SQL read paths and counts. Invalid numeric ranges in shared URLs are rejected. Event titles render through the dataset field mapping. Dark-mode chart theme and map label halo are fixed
  • Accounts: account deletion scheduling, cancellation and execution are serialized, all due accounts are processed, and verification and reset state is isolated per token

[0.1.0-alpha.9] - 2026-06-30

A large stabilization release. Five multi-agent bug sweeps across correctness, security, privacy, internationalization, and ingest/scraper robustness, plus ICU pluralization and localization of the remaining hardcoded copy. Closes the interpretation follow-ups from alpha.7 and hardens the production image build.

Added

  • Background job that reclaims and sweeps orphaned ingest CSV files
  • timetiles check verifies cron and systemd path references

Changed

  • Count strings use ICU plurals; page metadata, account-deletion copy, and remaining hardcoded strings localized (EN/DE)
  • Draft preview restricted to content roles (admin/editor); draft content is no longer served to anonymous visitors

Fixed

  • Security and privacy: drop all credential headers on cross-origin redirect; isolate the URL-fetch cache by auth identity; cap quota-remaining to block privileged-account enumeration; close a private-event leak in the H3 cluster branches; pair the pinned SSRF dispatcher with undici fetch (and stop it blocking public fd*/fc* domains); add X-Frame-Options and frame-ancestors CSP; restrict credential reads and rotate sessions on change
  • Accounts and auth: allow cancelling a scheduled deletion; defer email swap to verification time to prevent lockout; re-validate deletion eligibility at execution; unbreak email verification and enforce isActive; stop faking reset-email success
  • Ingest and schema: make dataset-detection idempotent and findOrCreateDataset concurrency-safe; prevent duplicate data-package activations under concurrency; reuse the latest schema version when unchanged and persist real enum frequencies; fail oversized imports terminally instead of stranding them; fix re-import data loss and progress/review stalls; numerous import-wizard and edit-schedule state fixes
  • Scrapers: recover stuck and orphaned runs; coerce off-contract runner output; RFC 4180 CSV escaping in the SDK writer; handle webhook-queue failures; make scraper deletion (and account-deletion coverage) work
  • Maps, charts, and explore: derive H3 resolution from returned cells; fix histogram tooltip dates, bar clicks, and locale; clamp range-slider bounds and keep drags alive with pointer capture; validate explore URL filter params to prevent crashes; apply range and cluster filters consistently across views
  • Quota and events: stop double-releasing event quota; pin the daily reset to a UTC boundary; clear the quota claim after a successful create
  • Geocoding: apply provider confidence grades correctly; preserve hemisphere for -0° DMS coordinates; score by result type and gate bias params
  • Deploy and build: pin timescrape’s zod so the production image builds; thread DEFAULT_LOCALE into image builds; ship health-check.sh and alert.sh as tracked files

[0.1.0-alpha.8] - 2026-06-04

A same-day follow-up to alpha.7: data-package auto-activation in deployed environments and map-rendering fixes.

Added

  • Auto-activate the three bundled data packages on staging and production

Fixed

  • Preserve antimeridian-crossing map bounds and accept wrapped world-view bounds; use map glyphs that exist
  • Unbreak the map CSP (allow the worker blob: source and the bare VersaTiles host)

[0.1.0-alpha.7] - 2026-06-03

Introduces a unified row-interpretation pipeline (ADR 0040) that fixes silent data misreads — day/month date order, single-column combined coordinates, and locale-aware numbers — plus numeric range filtering on event fields. Includes two large multi-agent bug sweeps.

Added

  • Numeric range filtering on event fields, with EU-aware decimal and thousands parsing
  • Single-column combined coordinates (e.g. "52.5, 13.4" in one field), with a review gate for ambiguous axis order
  • Per-column date day/month order detection with a review gate, fixing a silent DD/MM vs MM/DD misread
  • Locale-aware number parsing during record merge
  • Best-effort opt-in for ambiguous interpretations (ADR 0040)

Changed

  • Row interpretation unified into a single persisted interpretation plan — coordinate, date, and number handling now flow through one path (ADR 0040, Phases 0–4)
  • Layered-architecture ESLint boundaries now actually enforce the lib/ layering

Fixed

  • Two multi-agent bug sweeps (a 28-bug sweep and a backend correctness/security/rate-limit sweep): event quota charged only for newly-created events, locale-independent sort keys for dedup and cache, committed sub-batches retained on partial bulk-insert failure, import config validation (ID-strategy and mapping vs transforms), and map labels/antimeridian/single-day timezone/virtualization
  • Salvage swapped axis order for ambiguous combined coordinates
  • Deploy: writable tmpfs for the Next.js .next/cache; scraper returns 409 before 429 on an in-progress run

[0.1.0-alpha.6] - 2026-05-12

Tightens the authenticated surface: data-packages moved behind the account section, optional public contact email on the disabled-registration screen, and the Payload migration history is reconsolidated so migrate:create stops rediscovering historical changes.

Added

  • contactEmail field on the Branding global, rendered as a mailto link on the disabled-registration screen so visitors know how to request an account
  • timetiles check flags drift between deployed deployment/ files and the repository checkout

Changed

  • /data-packages is now /account/data-packages and requires authentication; the link moved from the main menu to the header user dropdown
  • Snapshot baseline reset migration so payload migrate:create starts from the current schema instead of rediscovering changes from earlier hand-written migrations

Removed

  • Unused retry-tracking columns (retry_attempts, last_retry_at, next_retry_at, last_successful_stage) and dependent enums from ingest_jobs / _ingest_jobs_v, catching up a code-vs-schema drift

Fixed

  • timetiles check SSL, podman, and run_check robustness; false negatives in verify_ufw, verify_fail2ban, and verify_backup_cron

[0.1.0-alpha.5] - 2026-05-11

First post-funding-period release. Operational hardening for the public launch: deployment robustness, geocoding correctness, CMS bootstrap, and image-supply-chain security.

Added

  • Environment banner on non-prod deployments
  • Auto-activation of data packages on boot per environment
  • Bundled data-package manifests delivered via git pull on deploy
  • Auto-bootstrap of free-tier geocoding providers on first boot
  • Default CMS content bootstrapped as part of the deploy seed preset
  • Demonstrations.org data packages with POST pagination
  • UCDP HDX data package and catalog metadata
  • NSU Offener Prozess scraper example
  • Transactional emails queued via Payload tasks
  • Storybook stories for UI primitives, charts, layout/editorial blocks, overlays, navigation, map controls, and form/feedback components
  • LOGIN_SUCCESS, LOGIN_FAILED, and REGISTERED audit events
  • Points cluster display mode
  • Smart groupBy filtering with shared settings panel
  • URL-sync for fullscreen state and groupBy UX
  • Contextual category filters with cross-filtering
  • Multi-column grid layout (renamed from List view)
  • Collapsible chart section with unified header
  • CardToolbar primitive and “Analyze” chart button
  • Info popover on catalog group headers
  • Split-to-array transform for multi-value CSV fields
  • E2E_MODE signal and isE2E() helper
  • ESLint rule preventing sql.raw() with dynamic strings
  • SonarCloud scripts: coverage-per-file, security-hotspot review, quality-gate fetch

Changed

  • Wizard store actions renamed to intent-based names
  • Trivy scan and wait_for_health extracted into reusable CI helpers
  • Explore view-toggle terminology unified on “List”
  • editDatasetId replaced with editScheduleData state
  • Documentation <img> tags converted to next/image
  • Favicon metadata and assets refreshed
  • Seed bootstrap consolidated into the deploy preset of SeedManager

Fixed

  • Scheduled-ingest retries, quotas, and URL-fetch hardening regressions
  • CSV identity values preserved through import
  • Map and import flows hardened (replacing fake tests with real assertions)
  • Maintenance job task enum values aligned with code
  • Numeric strings no longer accepted as inferred dates; trusted HTTP date guard simplified
  • Deployment: nginx healthcheck timing, postgres race on first boot, SMTP cert mismatch, container UID for logs//uploads/, DEPLOYMENT_ENVIRONMENT propagated to the web container, post-pull re-exec
  • schemaMode propagation in wizard first-run and scraper paths
  • “Create new catalog/dataset” placed at top of dropdown
  • Spreadsheet-prefixed coordinates (e.g. =52.5, 13.4) now parse correctly during import
  • Duplicate retry-exhausted notification emails on scheduled ingests
  • Array-form callback emitted for pinned undici hostname lookup

Security

  • Scheduled ingest validation hardened
  • HTTP security headers baseline and SSRF/path-traversal hardening on import
  • Critical and high-priority findings from the 2026-04-16 audit closed
  • Docker image security scanning hardened (Trivy fails the build on findings)
  • Seeded user credentials moved out of tests/ so they no longer ship in production builds

[0.1.0-alpha.4] - 2026-03-31

End-of-funding-period iteration. Final state of the platform on the last day of the Prototype Fund Software Sprint funding period (FKZ 16IS25S03).

Added

  • H3 hexagonal grid clustering with expert mode
  • Cluster focus mode, H3-based cluster filtering, mini dashboard
  • Unique-locations model — separate locations from clusters
  • Dataset-centric filter sidebar with multi-catalog support
  • Dataset info popover, colored checkboxes, dataset bounds API
  • Auto-zoom to data on dataset selection change
  • Beeswarm chart type with d3-force layout and server-side temporal clustering
  • Grouped charts: groupBy for beeswarm and stacked histogram
  • Chart type synced to URL parameter
  • Tag field support for multi-value array filtering
  • HTML-in-JSON import format with detail-page enrichment
  • JSON pre-processing with group-by and date merge
  • Embeddable views via /embed route
  • Photon and LocationIQ geocoding providers (weighted distribution)
  • Funding logos (Prototype Fund, BMFTR) in docs, web app, and README

Changed

  • Tag chips in event detail show field labels
  • Filter rows left-aligned; redundant catalog labels removed
  • Catalog chart hidden for single-catalog selections

Fixed

  • cluster_events() location_name cast to text

[0.1.0-alpha.3] - 2026-03-22

Second Second-Stage iteration. Feature work in direct response to feedback from the partner-organisation cooperation: internationalization, scraper sandboxing, JSON API ingestion, full wizard redesign.

Added

  • Internationalization with next-intl (English and German locales)
  • Translation of all user-facing components and email templates (EN/DE)
  • Payload CMS content localization for EN/DE
  • Custom scraper system with isolated Podman runner, scraper management UI, and CLI
  • Scraper quota enforcement
  • Block registry, UI customization, and layout templates
  • Branding global wired into emails (siteName and logo)
  • API key authentication
  • Configurable legal notices on the registration form
  • Timezone support for scheduled imports
  • JSON API import with pagination and wizard UX
  • JSON file upload in the import wizard
  • Edit scheduled import via the import wizard
  • Multi-stage progress timeline and column-centric mapping in the import wizard
  • Multi-worker support with concurrency control for jobs
  • Error boundaries on explore and import pages
  • React Compiler enabled

Changed

  • Renamed service factories to create*Service convention
  • Replaced custom fade animations with native View Transitions API
  • Migrated all frontend routes to locale-aware [locale]/(frontend)/ structure
  • Renamed import → ingest across the codebase
  • Schema-detection package consolidated into the app

[0.1.0-alpha.2] - 2026-03-12

First Second-Stage iteration. Code quality, infrastructure hardening, and platform consolidation in preparation for the partner-cooperation work.

Added

  • Restic-based backup system with S3 offsite support
  • All-in-one Docker image for single-server deployments
  • Pre-built GHCR images in docker-compose, pull-based deployment workflow
  • Integration tests for nginx routing, backup/restore, and Docker container lifecycle
  • Forgot-password and reset-password pages
  • Generic audit log with broad codebase coverage (extends the original audit-logging service)
  • apiRoute() framework with auto-wrapping; all API routes migrated
  • Zod validation schemas across all API routes
  • Encryption of credentials at rest
  • Sites collection wired into layouts with data-scope filtering
  • Worktree-management script with env-file setup
  • 80 additional oxlint native rules
  • Shared ContentState component for empty/error/no-match states

Changed

  • Decomposed large files into focused modules
  • Logo and asset reorganisation with new naming and folder structure

Fixed

  • Access control hardening (5 audit findings, including a security bug)
  • Dataset-schemas access control denormalised for performance
  • Constant-time email change, quota caching, env naming

[0.1.0-alpha.1] - 2025-12-02

First feature-complete public alpha at the close of the regular Prototype Fund Software Sprint Phase 1 (June–November 2025, FKZ 16IS25S03). Implements the milestones from the original proposal: design system, geocoding/data ingest, layout components, filters, visualisation, documentation.

Added

  • Initial public alpha of TimeTiles
  • Interactive geospatial timeline visualization with MapLibre GL JS
  • Data import system supporting CSV, Excel, and ODS formats
  • Automatic schema detection and field mapping
  • Multi-provider geocoding (Nominatim, Google) with database-driven provider configuration and persistent location cache
  • Real-time map clustering and timeline scrubbing
  • Timeline histogram for event distribution over time
  • Dynamic filters by category, date, and location with URL-based state
  • Categorical field filters with schema-based enum support
  • Map theme toggle and zoom controls
  • Adaptive tooltip formatting for histogram chart
  • Responsive web interface with light/dark themes
  • Mobile navigation drawer and filter sheet
  • Import wizard with field-mapping detection and user usage tracking
  • Transforms UI and flow editor
  • Scheduled URL-based imports with automatic execution and webhook triggers
  • Background job runner
  • Account management, feature flags, and branding globals (logos, favicons, site name)
  • Newsletter signup integrated with Payload CMS in the footer
  • Pages collection and MainMenu global for CMS-driven navigation; flexible page builder with block-based content
  • Multi-user permission and quota system with trust levels (0–5)
  • Owner-based access control for private data
  • Comprehensive audit logging service
  • Views collection for saved display configurations
  • Comprehensive E2E testing with Playwright
  • PostgreSQL 17 + PostGIS 3.5 backend
  • Next.js 16 frontend with Payload CMS 3 (integrated mode)
  • Documentation site (Nextra)
  • AGPL-3.0 license

For development changes, see the Git commit history .

Last updated on