Markdown Editor

Data Lineage Specification Template (OpenLineage)

Lineage use cases, capture granularity, OpenLineage 2-0-2 events and required facets, dataset and job naming, producers, store, consumers, lineage checks

Datentechnik & Daten-GovernanceVollständigOpenLineage specification 2-0-2lineagespecification

Anleitung: Use it when you introduce or standardise lineage capture across orchestrators and engines. Fill the use cases and the naming conventions first, because they decide what to capture and whether graphs from different tools join. The main pitfall is a lineage graph nobody trusts because producers silently stop emitting after upgrades.

Vorschau

Data Lineage Specification: [Organisation or platform name]

Specification for capturing, storing and using data lineage across a data platform: the questions lineage must answer, capture scope and granularity, the OpenLineage event model (specification 2-0-2) with required facets, dataset and job naming rules, producers and integration points, the lineage store and its consumers (impact analysis, incident triage, catalogue, privacy and audit), quality checks for lineage itself, and rollout. Replace every [bracketed] value and every row marked Example. Worked examples use Corvid Commerce, a fictional SaaS retailer with Airflow orchestration, Spark and dbt on an Apache Iceberg lakehouse and a Snowflake warehouse for finance.

Document control

FieldValue
Document ID[SPEC-LIN-001]
Version[0.1]
Status[Draft / In review / Approved / Superseded]
Owner[@platform-lead]
Approvers/reviewers[@head-of-data-governance], [@analytics-engineering-lead], [@security-architect], [@data-protection-officer]
Scope[Platforms, engines and environments covered]
Lineage standardOpenLineage specification 2-0-2 (https://openlineage.io/spec/2-0-2/OpenLineage.json)
Lineage backend[Tool name and version]
Effective fromYYYY-MM-DD
Next scheduled reviewYYYY-MM-DD
Last updatedYYYY-MM-DD
Classification[Internal]

Approvals

RoleNameDecisionDate
Platform lead[@name][Approve / Approve with conditions / Reject]YYYY-MM-DD
Head of data governance[@name][Decision]YYYY-MM-DD
Security architect[@name][Decision]YYYY-MM-DD

Revision history

VersionDateAuthorChange summary
0.1YYYY-MM-DD[@name]First draft
1.0YYYY-MM-DD[@name]Example: Approved; Airflow, Spark and dbt integrations mandatory
1.1YYYY-MM-DD[@name]Example: Column-level lineage required for Gold datasets

Contents

1. Purpose and scope

[Two to four sentences on the problem and the outcome.]

Example: When the Orders data product published wrong totals in [month], it took two days to find which dashboards and finance extracts had read the bad partition. Corvid Commerce will capture lineage at run time from the orchestrator and every engine that writes shared datasets, so that the owner of a broken dataset can list affected consumers within minutes, and governance can show where personal data flows.

ItemIn scopeOut of scope (for now)
EnginesExample: Airflow, Spark, dbt, Kafka Connect sinksExample: Ad hoc notebooks, spreadsheet exports
StoresExample: Iceberg lakehouse, Snowflake finance warehouse, Kafka topicsExample: Operational databases (captured only as sources)
EnvironmentsExample: ProductionExample: Development and staging (separate namespace, not retained)
ConsumersExample: BI dashboards registered in the catalogueExample: Personal BI workbooks

2. Lineage use cases and questions

IDUse caseQuestionNeedsPrimary user
LUC-001Impact analysis before changeWhich datasets, jobs and dashboards read this column or table?Column-level downstreamProducing team
LUC-002Incident triageA dataset is wrong: which upstream run produced it and what else did that run write?Run-level, with run status and timeOn-call data engineer
LUC-003Root causeWhich source and transformation produced this value?Column-level upstream with transformation typeAnalytics engineer
LUC-004Personal data mappingWhere does personal data from the customer system end up?Dataset-level downstream plus classification tagsData protection officer
LUC-005Deletion and retentionWhich copies must be deleted when a record is erased at source?Dataset-level downstream, including extractsPrivacy engineering
LUC-006Regulatory and audit evidenceHow is this reported figure produced, from source to report?Column-level, versioned, with code locationFinance controller, auditor
LUC-007Cost and clean-upWhich datasets have no readers in 90 days?Dataset-level downstream with last read timePlatform team
[LUC-nnn][Use case][Question][Granularity][User]

3. Capture scope and granularity

LevelDefinitionRequired forMethod
Dataset-levelJob X read datasets A and B and wrote dataset CAll shared datasetsRun events from engines
Run-levelWhich run (with start, end, status) produced which version of CAll production jobsRun events with run ID and parent run
Column-levelColumn C.total derives from A.amount and B.rate, with transformation typeSilver and Gold datasets, key fields only (Example)Column lineage facet from Spark and dbt integrations
Design-timeDeclared inputs and outputs of a job, without a runJobs not yet scheduled, external systemsJob events or catalogue entries
ManualEdge entered by a person with a reasonSystems that cannot emit, such as a vendor SFTP dropCatalogue form, reviewed every 6 months

Rules:

  1. Run-time lineage wins over manual lineage for the same edge. Manual edges show a "manual" marker and the date they were last confirmed.
  2. Every shared dataset in the catalogue has at least one producing job in the lineage graph, or an approved exception.
  3. Development and test runs are emitted to a separate namespace and are not shown to consumers.

4. Event model

OpenLineage defines three kinds of event:

EventPurposeWhen this platform emits it
Run eventState of one run of a job, with its input and output datasetsEvery production job run: start and end, plus intermediate states where the integration sends them
Job eventDesign-time description of a job, not tied to a runWhen a job is deployed or its declared inputs and outputs change
Dataset eventDesign-time metadata about a dataset, outside any runWhen a dataset is created, its schema changes outside a job, or it is dropped

Core entities and identity:

EntityIdentified byNotes
JobNamespace and nameName unique within the namespace (section 6)
RunrunId, a UUID generated by the clientUUIDv7 recommended by the specification; kept for the whole run
DatasetNamespace and name derived from the physical locationSame dataset seen by two tools must get the same pair

Run event types in specification 2-0-2 are START, RUNNING, COMPLETE, ABORT, FAIL and OTHER. A run normally sends START and then exactly one of COMPLETE, FAIL or ABORT.

4.1 Example run event

Example: the dbt model that builds the Orders mart completes. Values are illustrative.

{
  "eventType": "COMPLETE",
  "eventTime": "2026-10-30T06:12:44.120Z",
  "producer": "https://github.com/example-org/corvid-lineage-producer/v1.4.0",
  "schemaURL": "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/RunEvent",
  "run": {
    "runId": "0192d5a8-7c3e-7b21-9f40-3a1c2e5d8b77",
    "facets": {
      "parent": {
        "_producer": "https://github.com/example-org/corvid-lineage-producer/v1.4.0",
        "_schemaURL": "https://openlineage.io/spec/facets/1-2-0/ParentRunFacet.json#/$defs/ParentRunFacet",
        "run": {"runId": "0192d5a1-0b2f-7e55-8c10-4f7a9d3e2c11"},
        "job": {"namespace": "airflow-prod", "name": "orders_daily.dbt_build"}
      }
    }
  },
  "job": {
    "namespace": "dbt-prod",
    "name": "sales.marts.fct_orders"
  },
  "inputs": [
    {"namespace": "iceberg://lake-prod", "name": "sales.staging.stg_orders"},
    {"namespace": "iceberg://lake-prod", "name": "sales.staging.stg_fx_rates"}
  ],
  "outputs": [
    {"namespace": "iceberg://lake-prod", "name": "sales.marts.fct_orders"}
  ]
}

The facet _schemaURL versions above are illustrative; record the versions your client library actually emits in section 5.

5. Required facets

Every facet carries _producer and _schemaURL. This platform requires the following:

IDFacetAttached toRequirementServesNotes
FAC-001parentRunRequired when the run is started by an orchestratorLUC-002Links a dbt or Spark run to the Airflow task
FAC-002nominalTimeRunRequired for scheduled jobsLUC-002Scheduled time, not actual start
FAC-003errorMessageRunRequired on FAILLUC-002Message and stack trace; scrub values (section 11)
FAC-004sqlJobRequired for SQL jobsLUC-003, LUC-006Query text; no literals with personal data
FAC-005sourceCodeLocationJobRequiredLUC-006Repository URL, path and commit
FAC-006jobTypeJobRequiredAllBatch or streaming, integration, job type
FAC-007schemaDatasetRequired for outputsLUC-001Field names and types
FAC-008dataSourceDatasetRequiredAllName and URI of the source system
FAC-009columnLineageOutput datasetRequired for Silver and Gold (Example)LUC-001, LUC-003, LUC-006Transformation type and subtype per input field
FAC-010ownershipDataset, jobRecommendedLUC-002Owner names; catalogue remains the source of truth
FAC-011lifecycleStateChangeDatasetRequired on create, drop, truncate, renameLUC-005, LUC-007Drives clean-up and deletion checks
FAC-012dataQualityAssertions or dataQualityMetricsInput datasetRecommendedLUC-002Links quality results to the run
FAC-013outputStatisticsOutput datasetRecommendedLUC-002Row counts and bytes written
FAC-014symlinksDatasetRequired where one dataset has two names (table and path)AllJoins the graph across naming schemes
FAC-015tagsDatasetRecommendedLUC-004Classification tags copied from the catalogue

5.1 Column-level lineage transformation types

The column lineage facet records, for each output field, the input fields and how they were used. The specification distinguishes:

TypeSubtypesMeaning
DIRECTIDENTITY, TRANSFORMATION, AGGREGATIONThe output value is computed from the input value
INDIRECTJOIN, GROUP_BY, FILTER, SORT, WINDOW, CONDITIONALThe input affects the output without being part of its value

A masking flag marks inputs that were hashed or otherwise obfuscated on the way to the output; LUC-004 uses it to show where personal data is pseudonymised.

Example: fct_orders.order_total_eur has a DIRECT TRANSFORMATION from stg_orders.order_total and stg_fx_rates.rate, and an INDIRECT JOIN from stg_fx_rates.currency_code.

6. Naming conventions

6.1 Datasets

StoreNamespace patternName patternCorvid value (Example)
PostgreSQLpostgres://{host}:{port}{database}.{schema}.{table}postgres://orders-db.internal:5432, orders.public.order_lines
Snowflakesnowflake://{organization name}-{account name}{database}.{schema}.{table}snowflake://corvid-finance, FIN.CORE.REVENUE_DAILY
BigQuerybigquery{project id}.{dataset name}.{table name}Not used
S3s3://{bucket name}{object key}s3://corvid-vendor-drop, marketing/spend/2026-10-29.csv
Kafkakafka://{bootstrap server host}:{port}{topic}kafka://kafka-prod.internal:9092, checkout.events.v2
Iceberg tables in a catalogue (local rule)iceberg://{catalogue name}{namespace}.{table}iceberg://lake-prod, sales.marts.fct_orders

The Snowflake row follows the organisation and account name form that the OpenLineage naming page recommends over the legacy account locator; switching later breaks existing graph links. Where the specification does not list a store (the Iceberg row), record a local rule here and attach a symlinks facet that maps the table to its storage path. Check the current naming page before adding a store.

6.2 Jobs

ProducerJob namespaceJob name pattern
Airflowairflow-[env]{dag_id}.{task_id}
Sparkspark-[env]{appName}.{command}.{table} as produced by the integration
dbtdbt-[env]{project}.{folder}.{model} (local rule)
Kafka Connectconnect-[env]{connector name} (local rule)

Rules: one namespace per producer and environment; never reuse a job name for a different job; renames are announced and recorded as a symlink or alias in the backend.

7. Producers and integration points

IDProducerIntegrationVersionLineage levelOwnerStatus
PRD-001 (Example)AirflowAirflow OpenLineage provider[version]Dataset, run, parentPlatform teamLive
PRD-002 (Example)SparkOpenLineage Spark integration[version]Dataset, columnPlatform teamLive
PRD-003 (Example)dbtOpenLineage dbt integration run as a wrapper[version]Dataset, column, sqlAnalytics engineeringLive
PRD-004 (Example)Kafka ConnectCustom emitter from connector config[version]DatasetStreaming teamPlanned
PRD-005 (Example)Vendor SFTP dropManual edge in catalogueNot applicableDatasetMarketing data stewardLive, reviewed every 6 months
[PRD-nnn][Producer][Integration][version][Level][@owner][Status]

Producers send events over HTTP to the collector endpoint with an API key per producer, or to a Kafka topic when the network does not allow direct HTTP.

8. Lineage store and architecture

ComponentChoiceRetentionNotes
Collector[Tool]Not applicableValidates events against the 2-0-2 schema; rejects events without run.runId or dataset names
Graph store[Tool]Example: Run events 400 days; current graph kept indefinitelyLong enough to answer year-end audit questions
Dead letter store[Tool]Example: 30 daysReviewed daily by the platform on-call
Catalogue sync[Connector]Not applicablePull every 15 minutes (see data/data-catalog-metadata-standard section 9)

9. Consumers and access

ConsumerUse casesAccess pathAccess level
Producing teamsLUC-001Pull request check lists downstream assets and owners of changed columnsAll shared assets
On-call data engineersLUC-002Incident tool links from an alert to the run and its downstream datasetsAll
Analytics engineersLUC-003Catalogue lineage view, column levelAll
Data protection officerLUC-004, LUC-005Privacy data map filtered by personal data tagsAll, including restricted datasets
Finance and auditorsLUC-006Exported, versioned lineage report for named figuresNamed Gold datasets only
Platform teamLUC-007Unused dataset reportAll

The impact check in pull requests works as follows:

10. Lineage quality and monitoring

IDCheckDefinitionTarget (Example)Action on breach
LQ-001Producer coverageShared datasets with at least one producing job in the graph, divided by shared datasets98%Ticket to dataset owner
LQ-002Run completionRuns with a START and a terminal event within 24 hours, divided by runs started99.5%Investigate integration
LQ-003Orphan datasetsDatasets read by jobs but with no producer and no manual edge0 for GoldAdd producer or manual edge
LQ-004Name collisionsSame physical table under two names without a symlink0Fix naming in producer
LQ-005Column lineage coverageKey fields of Silver and Gold datasets with column lineage100% Gold, 90% SilverOwner action
LQ-006FreshnessTime from run end to event in the graph95th percentile under 5 minutesPlatform alert
LQ-007Invalid eventsEvents in the dead letter store per dayUnder 0.1% of eventsFix producer

Example: in week 2026-W43 Corvid had 1,240 shared datasets, of which 1,221 had a producer in the graph, giving LQ-001 coverage of 98.5%.

11. Security and privacy

IDControlDetail
LSEC-001No data values in facetsSQL text and error messages are scrubbed of literals; producers must not send sample rows
LSEC-002Authenticated producersOne API key per producer, rotated every [90] days, scoped to its namespaces
LSEC-003Access follows the catalogueUsers see lineage nodes for datasets they may discover in the catalogue; restricted nodes show as "restricted" with owner only
LSEC-004IntegrityEvents are append-only; manual edges carry author and date
LSEC-005RetentionRun history kept as in section 8, then deleted

Lineage is processing metadata, but job names, SQL and error messages can still contain personal data. Treat the lineage store at the highest classification of the metadata it may hold.

12. Rollout plan

PhaseWindowScopeUse cases deliveredExit criteria
1 (Example)Months 1 to 2Airflow and dbt, dataset-level, collector and graph storeLUC-002, LUC-007LQ-001 above 80%, LQ-002 above 99%
2 (Example)Months 3 to 4Spark, column-level for Gold, catalogue syncLUC-001, LUC-003Impact check live in CI for the Sales domain
3 (Example)Months 5 to 6Kafka Connect, manual edges, privacy data mapLUC-004, LUC-005DPO signs off the personal data flow map
4 (Example)Months 7 to 9Audit export, column lineage for SilverLUC-006Finance controller accepts the report for one reported figure

13. Glossary and references

13.1 Glossary

TermMeaning in this document
LineageThe record of which processes read and wrote which data, and when
Run eventA message that reports the state of one execution of a job and its inputs and outputs
FacetA named, versioned block of extra metadata attached to a run, job or dataset
NamespaceThe prefix that, with a name, identifies a job or dataset uniquely
Column-level lineageLineage between individual fields, with how each input was used
Impact analysisFinding everything downstream that a change could affect
SymlinkAn alternative name for the same dataset, used to join graphs

13.2 References

ReferenceVersion or date checkedUse
OpenLineage specification, JSON schema, https://openlineage.io/spec/2-0-2/OpenLineage.json2-0-2; checked 2026-10-30Event types, required fields of run, job and dataset
OpenLineage object model, https://openlineage.io/docs/spec/object-modelChecked 2026-10-30Run, job and dataset events; facet families
OpenLineage naming conventions, https://openlineage.io/docs/spec/namingChecked 2026-10-30Dataset and job naming patterns in section 6
OpenLineage column lineage facet, https://openlineage.io/docs/spec/facets/dataset-facets/column_lineage_facetChecked 2026-10-30Transformation types and subtypes, masking flag
[Lineage backend documentation][Version][Storage and API]

Standard and licence note

Based on the OpenLineage specification 2-0-2 and its published facet and naming documentation (OpenLineage, an LF AI and Data Foundation project, Apache License 2.0). Event types, field names, facet names and naming patterns are used as identifiers; no specification text is reproduced. The use cases, requirements, quality checks, local naming rules and examples are original, and Corvid Commerce is fictional. This template is original text written for this collection. Check the current OpenLineage specification and the documentation of your integrations before use. Template licence: MIT.

Ähnliche Vorlagen