SOURCES.YML

The sources.yml file declares where your raw data lives. It maps database tables to source references that your models can use with {{ source('name', 'table') }}.

THE CONNECTION: PROPERTY

The connection:property is DVT's key extension to dbt's sources.yml. Declared under the source's meta:block (dbt's schema only allows custom keys there), it links a source to a specific output in profiles.yml, telling DVT which database this data lives on.

Every source — including default-target ones

SHOULD have meta.connection:

Give every source a meta.connection naming its profiles.yml output. dbt ignores meta entirely, so it is harmless for standard models — and it is what federated models use to find the data. Bonus: when you later switch the default target, sources that already carry their connection keep working without edits.

The fallback when meta.connection is absent

source name must match an output name

If a source has no meta.connection, DVT falls back to the source's own name: a source called mysql_ops binds to the mysql_ops output. If neither resolves, federated models reading that source fail at extraction (DVT011). Standard models are unaffected — dbt resolves them from database/schema alone.

EXAMPLE

# models/staging/sources.yml
version: 2

sources:
  # ─── External Sources (need connection:) ───

  - name: crm
    description: "CRM data from MySQL operational database"
    meta:
      connection: mysql_ops              # ← REQUIRED: points to profiles.yml
    tables:
      - name: customers
        description: "Customer master data"
      - name: orders
        description: "Sales orders"
      - name: products

  - name: warehouse
    description: "ERP data from Snowflake"
    meta:
      connection: sf_warehouse           # ← REQUIRED: points to profiles.yml
    database: PROD_DB
    schema: RAW
    tables:
      - name: invoices
      - name: shipments
      - name: vendors

  - name: data_lake
    description: "Raw files from S3"
    meta:
      connection: data_lake              # ← works with bucket storages too
    tables:
      - name: web_events
      - name: app_logs

  # ─── Default Target Sources ───

  - name: analytics
    description: "Tables on the default PostgreSQL target"
    schema: public
    meta:
      connection: pg_dev                 # ← recommended even here: dbt ignores
    tables:                              #   it, and a future target switch is free
      - name: seed_categories
      - name: dim_dates
      - name: historical_metrics

HOW DVT USES SOURCES

The execution path is chosen by the model's materialized config — not by the sources. What meta.connection controls is where a federated model extracts each source from:

SOURCE CONFIGWHAT HAPPENSSQL DIALECT
Standard model (table / view / incremental)Sources on the default target: dbt resolves database/schema and runs natively. Sources on a foreign connection: DVT coerces the model to f_table / f_incremental automatically, with a warningTarget's native SQL (DuckDB SQL if coerced)
Federated model (f_table / f_incremental)Each source extracted from its meta.connection (or name-matched output) via Sling into DuckDBDuckDB SQL

A standard model should only read sources that live on the default target. The moment a model needs data from another engine, declare it federated yourself — DVT will coerce it for you rather than fail, but an explicit materialization states your intent and silences the warning. See f_table on the federated materializations page.

COMMON PATTERNS

Multi-source extraction model:

-- models/marts/cross_engine_report.sql
-- crm is on MySQL, warehouse is on Snowflake
-- Both get extracted → DuckDB joins them → result loads to default target

{{ config(materialized='f_table') }}

SELECT
    c.customer_name,
    i.invoice_date,
    i.total_amount
FROM {{ source('crm', 'customers') }} c          -- MySQL (extracted)
JOIN {{ source('warehouse', 'invoices') }} i      -- Snowflake (extracted)
    ON c.customer_id = i.customer_id

Default-target pushdown model:

-- models/staging/stg_categories.sql
-- analytics source has no connection: → pushdown on default target
-- SQL dialect: PostgreSQL (native)

{{ config(materialized='view') }}

SELECT id, name, parent_id
FROM {{ source('analytics', 'seed_categories') }}

Bucket storage source:

-- models/staging/stg_web_events.sql
-- data_lake source points to S3 → extracted via Sling

{{ config(materialized='f_table') }}

SELECT event_id, user_id, event_type, event_timestamp
FROM {{ source('data_lake', 'web_events') }}

SOURCE PROPERTIES REFERENCE

PROPERTYREQUIREDDESCRIPTION
nameyesUnique name for this source group
meta.connectionnoprofiles.yml output name. Recommended on every source; without it the source name itself must match an output for federated reads.
databasenoDatabase name (overrides the output's default)
schemanoSchema name (overrides the output's default)
descriptionnoHuman-readable description for documentation
tablesyesList of table objects with at minimum a name field
tagsnoTags for source selection (dvt run --select tag:...)
freshnessnoFreshness check configuration (standard dbt)

COMMON MISTAKES

WRONG

sources:
  - name: analytics
    connection: pg_dev           # ← top level: dbt REJECTS this
    tables:
      - name: dim_dates

CORRECT

sources:
  - name: analytics
    meta:
      connection: pg_dev         # ← under meta: dbt allows, DVT reads
    tables:
      - name: dim_dates

connection: must live under meta:. dbt validates sources.yml against its schema and errors on unknown top-level keys — meta is dbt's sanctioned extension point, which is exactly why DVT (a wrapper, not a fork) uses it.

WRONG

sources:
  - name: crm
    # Missing connection!
    tables:
      - name: customers        # lives on MySQL

CORRECT

sources:
  - name: crm
    meta:
      connection: mysql_ops      # external source
    tables:
      - name: customers

External sources must have connection:. Without it, DVT warns and falls back to a profiles.yml output named after the source ('crm') — and if no such output exists, extraction fails with DVT011. Add meta.connection to every source: one rule, no exceptions.