Required Asset Metadata#
Status: adopted as plan2 D14 (2026-10-30), resolving plan0 open question 11. Related:
plan2 D10 (governance), D11 (notebook access), D12 (lake convention), D13 (Delta)
plan0 open questions 10 (catalog registration) and 12 (policy source)
1. Purpose#
Dagster is where every asset is defined, so it is the one place to capture what the catalogs, lineage backends and access policies need. Metadata declared on the asset flows outward:
to OpenLineage events (our own emitter, D10), and from there to Marquez/OpenMetadata on-prem and DataZone on AWS;
to catalog registration (open question 10): Unity Catalog/HMS comments and properties, Glue table parameters;
to access policy (open question 12): sensitivity tags drive column and row rules, including Lake Formation tag-based access control (LF-Tags) and the matching OPA/Ranger rules;
to D11: whether a table can be read directly or only through the engine follows from its column sensitivity.
If metadata isn’t declared on the asset, it doesn’t exist anywhere downstream. So the required set is small, checked in CI, and mostly filled in automatically.
2. What Dagster 1.13 supports (verified 2026-10-30, image 3ef6d4e22d8f)#
@asset(description=, owners=, group_name=, tags=, kinds=, metadata=, code_version=).ownersmust be an email address orteam:<name>; a bare name is rejected.Custom tag keys with a namespace (
ods/domain,ods/sensitivity) are accepted.TableColumn(name, type, description, constraints, tags): column-level tags work.Standard table metadata (
TableMetadataSet):column_schema,column_lineage,row_count,partition_row_count,table_name,storage_kind.
3. The required set (every asset that writes to the lake)#
Field |
Where |
Rule |
Feeds |
|---|---|---|---|
Key |
|
D12 shape and names |
Everything |
Description |
|
Non-empty; the first line is a one-sentence summary |
Catalog comments; OpenLineage documentation facet |
Owner |
|
At least one |
OpenLineage ownership facet; DataZone/OpenMetadata ownership |
Domain |
|
The same value (snake_case); the group is for Dagster’s UI, the tag travels with lineage |
DataZone/OpenMetadata domains |
Table sensitivity |
tag |
One of |
Policy (open question 12), D11 |
Kinds |
|
Storage + compute, e.g. |
Dagster UI |
Source (raw only) |
tag |
The |
Lineage, catalog |
Columns (metadata={"dagster/column_schema": TableSchema(...)})#
Layer |
Requirement |
|---|---|
All layers |
Every sensitive column is declared at definition time with tag |
|
Every column is declared, with type and description |
|
Columns are recommended; sensitive ones are required (as above) |
|
Full schemas come from dlt at run time; only sensitive columns are declared |
Optional, recommended for marts#
A freshness policy (Dagster freshness checks).
code_version=on Python assets, so changes in code mark downstream assets stale.Tag
ods/tier:gold(contracted, monitored) orbronze(best effort).
4. Filled in automatically at materialization#
The lake I/O manager and resources add these, so authors never write them:
Key |
Value |
|---|---|
|
|
|
|
|
From the write |
|
The actual Delta schema after the write |
|
|
|
The table version this run committed, for reproducibility and time travel |
5. Sources of the metadata, by asset type#
Python assets: declared directly on
@asset. A small helper (ods_asset(...)or aDomainSpec) fills in the defaults (domain → group + tag, kinds) so authors only supply what is specific to the asset.dbt models: declared in
schema.ymland mapped by aDagsterDbtTranslator:models: - name: customer_revenue description: Revenue per customer per month. config: meta: ods: {owner: "team:finance", domain: finance, sensitivity: internal} columns: - name: email description: Customer contact email. config: meta: ods: {sensitivity: confidential, pii: email}
dbt’s own column descriptions satisfy the
martsrule, anddbt buildtests become asset checks.dlt sources: the dlt translator sets owner, domain,
ods/sourceand table sensitivity per source. Sensitive columns are declared in a per-source mapping next to the source definition.
6. Enforcement#
Definitions test (CI,
tests/definitions/) loadsDefinitionsand fails on:missing or invalid description, owner, domain or sensitivity;
group ≠
ods/domain;table sensitivity lower than its highest column sensitivity;
a
martstable with undeclared columns;D12 key and naming violations.
an input of a Delta-managed asset with no type annotation (D5 addendum).
Schema-drift asset check (runtime,
marts): the declared columns must match the materialized Delta schema. It fails on a missing or undeclared column, so a column can’t be added without a description and sensitivity.PII backstop (D10): the Presidio check flags data that looks like PII in a column not tagged as sensitive. That catches declarations that are wrong, not just missing.
7. How it maps outward#
Our field |
OpenLineage |
DataZone / Glue |
OpenMetadata / Unity Catalog |
Policy |
|---|---|---|---|---|
description |
documentation facet |
asset/table description |
description / comment |
n/a |
owners |
ownership facet |
owner |
owners |
approver for access requests |
|
tags / custom facet |
domain |
domain |
rule scope |
|
tags / schema facet |
LF-Tag |
classification tag |
LF-TBAC / OPA rule input |
|
schema facet |
Macie comparison |
PII tag |
masking rules |
column schema |
schema facet |
Glue columns |
columns |
column grants |
Driving access from tags rather than column lists (Lake Formation LF-Tags; OPA rules keyed on tags) means a new confidential column is protected the moment it is declared. That is the main input to open question 12.
8. Changes needed in code (Phase 2)#
Add the
ods_assethelper (or a spec builder) indagster_platform/.Lake I/O manager: add the runtime metadata in §4. It subclasses the multi-type
DeltaLakeIOManager(D5).dlt and dbt translators: map the metadata above.
tests/definitions/test_conventions.py: the D12 + §6 checks.The smoke asset gets owner
team:platform, domainplatform, sensitivitypublic.