viur-core 3.9 released!

ViUR-core 3.9 was released on September 11, 2026, and brings a substantial datastore performance overhaul, several new bones, a reworked email and rate-limiting story, and a handful of notable changes and improvements that developers will want to be aware of.

  • A faster, more flexible datastore layer. db.get/db.put/db.delete now sit on top of an optional Memcache-backed cache layer, so repeated reads for the same keys within a request no longer round-trip to the datastore. On top of that, Cloud Datastore's support for multiple named databases and namespaces is now exposed directly through conf.db.name and conf.db.namespace, letting a single codebase target different environments without code changes.
  • Native operators instead of client-side MultiQuery. Filters like IN, !=, and NOT_IN used to be split into several sub-queries and merged in Python; they're now sent to Cloud Datastore as native operators, cutting RPC counts and fixing ordering issues that the old approach had. A new Query.or_filter() lets you express OR-combined condition groups natively, and keys_only, typed QueryOrder, and the streaming iter_skel() round out the query toolkit for bulk operations and large result sets.
  • A more transaction-safe Skeleton API. Skeleton.patch() gained a preprocess hook that runs inside the same transaction right before the write, making custom edit flows easier to build without reimplementing the transaction dance. Alongside it, several correctness fixes landed: compute bones no longer fire during cascading deletes, and Skeleton.write() no longer resurrects bones that were deliberately removed at runtime.
  • New bones for content, money, and addresses. A tags parameter lets any bone be classified for privacy or audit tooling without affecting access control. A new CodeBone family (LogicsBone, JinjaBone, PythonBone) brings syntax-validated code storage for Logics, Jinja templates, and Python scripts. NumericBone gained exact decimal.Decimal support for money and other precision-sensitive values, and a new AddressBone handles structured addresses with automatic geocoding powered by OpenStreetMaps.
  • Hardening across many existing bones. CaptchaBone was migrated to reCAPTCHA Enterprise, TextBone and StringBone gained configurable HTML escaping, and RelationalBone writes are now batched for a measured 6-11x speedup on multi-relation saves. Beyond that, a long tail of bugs was fixed across BooleanBone, ColorBone, DateBone, EmailBone, FileBone, UidBone, RecordBone, and StringBone - mostly validation logic that silently did the wrong thing.
  • A standard module for outgoing email, and safer tree deletion. A new Email module tracks sent messages with predefined views for sent, unsent, and failed mail. Tree-structure deletion was reworked so an entire subtree is removed atomically via a single resumable job, instead of the previous approach that could strand orphaned child nodes if a deferred job was lost. User.Status also became a proper IntEnum, so project-specific extensions of it now compare correctly across codebases.
  • New building blocks in contrib, and better request-level security. A LoginKey auth provider adds token-based "magic link" login, and a RequestRateLimit validator throttles traffic per guest or user at the WSGI level, before routing even happens. On the security-header side, viur-core now emits the modern Reporting-Endpoints header, and CSRF security keys can be created in batches instead of one at a time.
  • A leaner admin API and a rewritten response cache. The previously separate vi/dumpConfig and vi/get_settings endpoints were merged into one vi/config, and a new vi/routes endpoint helps with debugging and security scanning. The old @enableCache decorator was completely rewritten as @ResponseCache, adding compression, redirect caching, and renderer-scoped caching, with a matching FlushCacheTask to clear it from the admin UI. New before_request/after_request hooks round out the request lifecycle.
  • Friendlier internationalization and documentation. conf.i18n.fallback_languages lets you define a chain of languages to fall back to before a translation's default text kicks in, and translation sources are now pluggable via conf.i18n.sources, so projects can mix static, datastore, or entirely custom sources. A first batch of Architecture Decision Records was also added under docs/adr/, documenting the reasoning behind several core bones.

For the full technical rundown, including code samples and configuration details for every change, see the complete What's new in viur-core 3.9 reference document - also available in german.