Skip to main content

Varda

CI PyPI Python Docs Status: experimental

Dimensional modeling for LinkML.

Experimental. Varda is published so it can be tried, not because the design has settled. The vocabulary will change, sometimes in ways that break a model written against an earlier version. Pin varda~=0.2.0.

Varda is a profile of LinkML: a small vocabulary that lets you say a class is a fact table, that a column is a semi-additive measure, that a dimension keeps history. It then checks those claims and generates from them.

A model annotated with Varda is still an ordinary LinkML schema. Every other LinkML tool — gen-pydantic, gen-owl, gen-json-schema — will read it happily and ignore what it does not understand.

FctSale:
  annotations:
    varda:role: FACT
    varda:fact_type: TRANSACTION
    varda:grain: [order_number, product_key]
    varda:grain_statement: one row per product per line of a receipt
  attributes:
    order_number:
      annotations:
        varda:role: DEGENERATE_DIMENSION
    product_key:
      range: integer
      annotations:
        varda:role: FOREIGN_KEY
        varda:references: DimProduct
    customer_key:
      range: integer
      annotations:
        varda:role: FOREIGN_KEY
        varda:references: DimCustomer
    net_amount:
      range: decimal
      unit:
        symbol: EUR
      annotations:
        varda:role: MEASURE
        varda:additivity: ADDITIVE
$ varda check model.yaml
8 tables checked against 43 rules (varda 0.2.0): 0 errors, 0 warnings

$ varda generate model.yaml --out out/
wrote out/docs/model.md
wrote out/sql/mart.sql

Install

pip install varda

Python 3.11+. The only runtime dependency is linkml-runtime.

What it gives you

Twelve annotations. Seven on tables — role, grain, grain_statement, hierarchies, fact_type, scd, physical_name. Five on columns — role, references, additivity, semi_additive_over, physical_name. That is the whole core vocabulary, and it is deliberately the whole core vocabulary. Units are LinkML's own unit, which Varda reads rather than restates.

Forty-three rules that catch the mistakes worth catching:

V001–V003 the annotations themselves — typos, bad enum values, unknown prefixes
V101–V134 structure — a fact without a grain, a foreign key pointing at a fact, a dimension with no natural key
V201–V206 measures — an unclassified measure, a semi-additive one that never says what it cannot cross

The V2xx family exists because additivity is where the expensive errors live. A structural mistake usually breaks a query. An additivity mistake returns a number that looks entirely reasonable and is wrong, to someone who will act on it.

Two generators, sql and docs, producing runnable DDL and a Markdown reference. Both are deterministic: no timestamps, no environment, same model in and same bytes out, so the output can be committed and diffed.

Extending it

Varda's core is small on purpose. Anything specific to how your organization works — cost centers, retention, data classification, ownership — goes in an extension under your own prefix.

The smallest useful extension needs no Python at all. Write a LinkML schema declaring your vocabulary, then a varda.toml:

[[extension]]
name = "acme"
prefix = "acme"
profile = "profiles/acme.yaml"

From then on acme:cost_center is a first-class annotation: checked for typos, its enum values enforced, and listed by varda ext. Misspell it and you get

ERROR V001  DimStore
        unknown table annotation 'acme:cost_center'; declare it in
        acme.yaml or fix the typo

Full documentation: https://mluttikh.github.io/varda/

An extension with code behind it adds rules and generators through varda.ext, and ships as an installable package advertising the varda.extensions entry point. See SPEC.md for the interface and tests/fixtures/acme_ext/ for a complete worked example.

One party, one namespace; extensions add, they never redefine. An extension may introduce annotations, enums and rules under its own prefix. It may not add a value to TableRole or change what SEMI_ADDITIVE means — every generator dispatches exhaustively on those, and the registry refuses at load rather than warning.

Commands

varda check MODEL validate; --strict fails on warnings too
varda generate MODEL --out DIR validate, then write artifacts; --force to build from a model that does not conform
varda rules list every rule, -v for reasoning
varda ext describe active extensions and their vocabulary
varda importmap print the LinkML import map

Exit codes are part of the contract: 0 success, 1 the model or run failed, 2 the invocation was wrong.

Status

0.2.0 — experimental. Published so it can be tried, not because the design has settled. Expect the vocabulary to change, sometimes in ways that break a model written against an earlier version. No warehouse of real size has been modeled in it yet, and no third party has written an extension. Pin it:

varda~=0.2.0

The cost of a break is bounded. A Varda model is annotated YAML, so a break is a find-and-replace over your schema rather than a migration of anything you have loaded, and the model stays an ordinary LinkML schema either way — gen-pydantic, gen-owl and the rest carry on regardless.

Two things are settled. Rule codes are permanent: none is renumbered, and a retired one is deleted rather than reused. And Varda will not grow syntax or fork the metamodel — the annotation-only design is the premise, not a stage.

Building the documentation

pip install -e ".[docs]"
mkdocs serve

Then open http://127.0.0.1:8000/varda/ — note the /varda/ path, which comes from site_url because this is a GitHub Pages project site rather than a user site. Plain http://127.0.0.1:8000/ redirects there.

The vocabulary, rules and command-line pages are generated from the package itself into a git-ignored docs/reference/, so they cannot drift from the code. mkdocs serve regenerates them on every rebuild and watches src/ as well as docs/ — edit a rule's docstring and the page updates.

To build the static site the way CI does:

python scripts/gen_reference.py
mkdocs build --strict

--strict turns a broken internal link into a failed build. The generator is a standalone script rather than a plugin, so the site also builds under Zensical and ProperDocs from the same mkdocs.yml — see docs/design.md for why that matters.

License

MIT for the code. The profile vocabulary in src/varda/profile/varda.yaml is CC0, so it can be reused anywhere without attribution — a vocabulary that constrains its own reuse is not much of a vocabulary.

Release files for varda 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for varda 0.2.0
File Size Uploaded
varda-0.2.0.tar.gz 76.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for varda 0.2.0
File Interpreter ABI Platform
varda-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 134.8 kB

Release files / varda-0.2.0.tar.gz

Download URL varda-0.2.0.tar.gz
Size 76.9 kB
Tags Source
SHA-256 checksum
How to use checksums
09b0e45438677eb7b3b3ff04a928d26da7934fe4be7d67e08b340158f5363657
BLAKE2b-256 checksum
How to use checksums
c62a232257130453f4a5678bd20509f726f078c12e3485e1fe738cdfb5526f89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release files / varda-0.2.0-py3-none-any.whl

Download URL varda-0.2.0-py3-none-any.whl
Size 57.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1122d8989a27e30722b4039baede031d3408b2ff93fdc866adaeab0aead46134
BLAKE2b-256 checksum
How to use checksums
843195a49726f1195514feb2ab21976455f26f1b5ab984286e29052a8801e01b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page