Sources¶
Sources define where a board reads data from. Most projects use one warehouse connection for SQL queries, but a board can also read files, HTTP endpoints, inline values, or dbt models.
Connection sources are defined once, in the project's root dbt_charts.yml, as a
named sources: registry. A board (or charts/meta.yml) never defines a
connection inline; it only references a source by name:
# dbt_charts.yml sources: analytics: type: dbt_profile profile: my_dbt_project target: dev
source: analytics # References the "analytics" source above queries: tickets: SELECT * FROM dundersign.tickets
Every sources: block on this page belongs in dbt_charts.yml, not in a board.
A board's own source: field always holds a name (or, for a single colocated
data file, an inline path; see Inline File Sources),
never a connection definition.
dbt Charts sources: are connection definitions. They are separate from dbt
sources: resources, which document raw tables inside a dbt project. If you are
setting up dbt source tables, use dbt's
source properties
reference.
Source Types¶
For warehouse-backed SQL, dbt Charts uses dbt-compatible connection settings. In
most dbt projects, use type: dbt_profile and let dbt own the adapter-specific
credentials. Direct warehouse source types use the same field names as dbt
profiles, but they are flat because boards read data; they do not build dbt
models.
You do not need profiles.yml for direct sources such as postgres,
snowflake, bigquery, duckdb, or sqlite. dbt Charts uses the direct source
block as the connection config. The dbt links below are still useful because
they document the same adapter credential fields.
| Source | dbt Charts setup | Credential fields / setup |
|---|---|---|
| Existing dbt project | type: dbt_profile |
dbt profiles.yml |
| BigQuery | type: bigquery |
BigQuery connection |
| DuckDB | type: duckdb |
DuckDB connection |
| Postgres | type: postgres |
Postgres connection |
| Redshift | type: redshift |
Redshift connection |
| Snowflake | type: snowflake |
Snowflake connection |
| SQL Server | type: dbt_profile |
dbt-sqlserver connection (install dbt-charts[sqlserver]) |
| Microsoft Fabric | type: dbt_profile |
dbt-fabric connection (install dbt-charts[fabric]) |
| Trino / Starburst | type: trino |
Trino (install dbt-charts[trino]) |
| Athena | type: dbt_profile |
dbt-athena connection (install dbt-charts[athena]) |
| ClickHouse | type: dbt_profile |
dbt-clickhouse connection (install dbt-charts[clickhouse]) |
| SQLite | type: sqlite |
dbt Charts direct source; no dbt setup |
| Databricks | type: databricks |
Databricks, token or OAuth service principal |
| Lake compute | type: lakecompute |
Lake compute (install dbt-charts[lakecompute]) |
| Spark | type: dbt_profile |
Apache Spark connection |
| dbt models | sql query using ref() with a dbt profile source |
dbt profiles.yml |
| CSV files | type: csv |
Files and APIs |
| Parquet files | type: parquet |
Files and APIs |
| JSON files | type: json |
Files and APIs |
| HTTP endpoints | type: http query |
Files and APIs |
| Inline static data | type: values query |
No source setup |
ClickHouse and max_execution_time. max_query_duration_seconds is a ceiling.
If your dbt profile's custom_settings already sets a stricter max_execution_time,
dbt Charts keeps yours; a looser one is capped at the ceiling. ClickHouse types the
setting as whole seconds and reads 0 as no limit, so a fractional or zero value is
rejected rather than silently disabling the cap.
ClickHouse read-only users need readonly = 2. dbt Charts caps every query with
ClickHouse's max_execution_time, sent as a per-request setting. A readonly = 1 user
cannot change any setting, so the connection fails at setup with ClickHouse's own
Cannot modify 'max_execution_time' setting in readonly mode. readonly = 2 is the
read-only mode ClickHouse gives BI clients: read-only queries, settings changes allowed.
SQL Server and query_timeout. max_query_duration_seconds is enforced through
your dbt profile's query_timeout, which T-SQL needs because it has no per-statement
SET. A stricter query_timeout of your own is kept; 0 means no cap and is replaced
by the ceiling. The mssql-python backend ignores query_timeout, so dbt Charts
refuses it rather than run uncapped; use the default pyodbc backend. SQL Server also
needs Microsoft ODBC Driver 17 or 18 installed on the host, which pip cannot provide. A query that fails
on SQL Server is not retried, including one that failed because an idle session was
dropped; run it again.
dbt Profile Sources¶
dbt Charts can either read your warehouse credentials from a dbt profile or accept a direct source definition with dbt-style field names.
Use dbt_profile when your project already has profiles.yml:
# dbt_charts.yml sources: analytics: type: dbt_profile profile: my_dbt_project target: dev
dbt Charts resolves profiles.yml in this order:
profiles_diron the source configDBT_PROFILES_DIR- The linked dbt project directory, next to
dbt_charts.ymlby default, or an external directory named via--dbt-project-dir/DBT_PROJECT_DIR/dbt_project_dir:(see--dbt-project-dir) ~/.dbt/profiles.yml
If none of those locations contains profiles.yml, dbt Charts raises an error.
Without a linked dbt project, steps 2 and 4 still apply (profiles_dir is relative to the project, so it needs one).
profile: is optional. When the source omits it, dbt Charts uses DBT_PROFILE,
then the profile: in the linked dbt project's dbt_project.yml, the same order
dbt uses. A board shipped inside a dbt package can therefore declare
type: dbt_profile and follow whichever profile the consuming project names. If
none of the three names a profile, the query fails with an error listing them.
No dbt_charts.yml needed inside a dbt project¶
When dbt_project.yml is at the project root and dbt_charts.yml declares no
sources: (or there is no dbt_charts.yml at all), dbt Charts adds one source
for you. It is named after the project's profile: and behaves as if you had
written type: dbt_profile for it, so it shows up in dct doctor and is usable
as source: <profile>. A SQL query that names no source: (on the query, at the
board root, or in a meta.yml) runs against it. Boards shipped in a dbt package
can leave source: out and follow whichever profile the consuming project names.
Declaring any sources: turns the implicit source off, and a SQL query that names
no source then fails with ERR-SOURCE-NOT-NAMED, as it does outside a dbt
project. A dbt_project.yml with no profile: (a dbt package) gets no implicit
source. On dbt Charts Cloud the implicit source appears as one unmapped
connection to bind; Cloud never reads your profiles.yml.
Which manifest ref() resolves against¶
target: picks the connection, but ref() and source() resolve against a dbt
manifest, and dbt does not record which target built it. With target: prod and a
manifest from dbt parse --target dev, queries run against dev relations. Point
the source at the manifest built for its target with target_path:, the same
meaning as dbt's --target-path, relative to the dbt project directory:
# dbt_charts.yml sources: analytics: type: dbt_profile profile: my_dbt_project target: prod target_path: target/prod
Without target_path:, the manifest is <dir>/manifest.json, where <dir> is
DBT_TARGET_PATH, then target-path: in dbt_project.yml, then target. The
directory must be relative to the dbt project (ERR-DBT-TARGET-PATH-INVALID
otherwise). A missing manifest at the resolved path raises ERR-DBT-MANIFEST-MISSING
naming it; dbt Charts never falls back to another one. A query inlined through
{{ queries.X }} resolves its ref() calls against the including query's source.
On dbtcharts.com¶
dbtcharts.com has no manifest to build and resolves ref() and source() against
your production warehouse only. It reads the model, seed, snapshot and source
definitions in your dbt project on the default branch, lists the tables in the
project's default source, and links each name to the table that exists there. A
target/manifest.json committed to the repository still wins. dbt macros in board
SQL are not supported: put the logic in a dbt model and ref() it.
A name that cannot be linked fails its chart with ERR-DBT-REF-UNKNOWN-NODE (or
ERR-DBT-SOURCE-UNKNOWN-TABLE) instead of guessing a table. The usual reasons:
- The model has not been built in production yet, or is ephemeral or disabled.
- Its schema, alias or database comes from Jinja or a macro rather than a literal
value in
dbt_project.yml, a properties file orconfig(). - Its name exists in more than one candidate schema, or the project overrides
generate_alias_nameorgenerate_database_name. - It lives in another database than the default source's.
A project with no default source, or whose first index is not built yet, fails
ref() charts with ERR-DBT-MANIFEST-MISSING. The index rebuilds when dbt files on
the default branch change and when the default source changes. A chart that fails on
a name the index lacks, on a missing manifest, or with ERR-BINDER-UNKNOWN-RELATION
(the warehouse reports the table or view does not exist) in a board that uses ref()
or source() also asks for a rebuild, so a table a production run creates or moves
is picked up by the next render. That request is made at most once every five minutes.
Who validates the target¶
profiles.yml is dbt's file, so dbt validates it; dbt Charts applies no schema
of its own. Every field your installed dbt adapter accepts works, including
threads and dbt's canonical database: / schema: spelling for BigQuery
(project: / dataset: work too, exactly as in dbt). A field dbt requires but
your target omits is reported when the dashboard runs, naming the profile and
target.
The flip side: a misspelled key is not an error. dbt ignores keys it does not
recognize, so hostt: localhost is silently unused, by dbt Charts and by
dbt run alike. If a connection behaves unexpectedly, check the spelling of your
target's fields.
This differs from the direct warehouse sources below, where dbt Charts owns the schema and an unknown key is an error.
Installing that adapter is a separate step: dbt Charts connects through a Python adapter package, which ships as an extra. See Warehouse adapters.
Direct Warehouse Sources¶
Direct warehouse sources are useful for small projects that do not share a dbt
project. This is the full connection config; no profiles.yml file is required.
For larger dbt projects, prefer type: dbt_profile.
Postgres (connect_timeout) and BigQuery (job_creation_timeout_seconds) give up
connecting after 5 seconds unless you set a different value. Redshift's
connect_timeout is the socket timeout for the whole connection, so unset it follows
max_query_duration_seconds plus a small headroom.
Postgres¶
# dbt_charts.yml sources: analytics: type: postgres host: "{{ env_var('ANALYTICS_HOST') }}" port: 5432 dbname: analytics schema: public user: "{{ env_var('ANALYTICS_USER') }}" password: "{{ env_var('ANALYTICS_PASSWORD') }}"
DuckDB¶
# dbt_charts.yml sources: local_db: type: duckdb path: ./data/analytics.duckdb schema: crm
DuckDB sources are local-only. A .duckdb file is a version-coupled,
single-writer binary, so dbt Charts Cloud does not read .duckdb files from
connected repositories. For Cloud, export the tables to Parquet or CSV and
register them as file sources instead.
SQLite¶
# dbt_charts.yml sources: local_sqlite: type: sqlite path: ./data/analytics.sqlite
Databricks¶
A Databricks source connects to a SQL warehouse and authenticates with exactly one of two credential sets: a personal access token, or an OAuth machine-to-machine (M2M) service principal. Databricks recommends the service principal for automation, and it is the only option when your admins have turned tokens off.
# dbt_charts.yml sources: db: type: databricks host: "{{ env_var('DATABRICKS_HOST') }}" http_path: /sql/1.0/warehouses/abc123def456 catalog: main schema: analytics client_id: "{{ env_var('DATABRICKS_CLIENT_ID') }}" client_secret: "{{ env_var('DATABRICKS_CLIENT_SECRET') }}"
For a personal access token, replace the two service principal fields with token:
# dbt_charts.yml sources: db: type: databricks host: "{{ env_var('DATABRICKS_HOST') }}" http_path: /sql/1.0/warehouses/abc123def456 catalog: main schema: analytics token: "{{ env_var('DATABRICKS_TOKEN') }}"
Setting both credential sets, only one of client_id and client_secret, or
neither is an error; dbt Charts never picks a method for you. Create the secret
under Settings, Identity and access, Service principals, Secrets in your
workspace, and grant the principal CAN USE on the SQL warehouse (see the
Databricks OAuth M2M guide).
A literal client_secret or token in dbt_charts.yml is rejected; use env_var().
connect_timeout is how many seconds a connection attempt may keep retrying while
the SQL warehouse starts, 15 by default. socket_timeout bounds each socket read
separately.
Trino¶
Trino connects through the trino Python client rather than a dbt adapter. Install
the extra (pip install "dbt-charts[trino]").
# dbt_charts.yml sources: lake: type: trino host: trino.example.com port: 8443 catalog: analytics schema: public user: ana method: ldap password: "{{ env_var('TRINO_PASSWORD') }}"
The field names are the keys of a dbt type: trino profile target. catalog and
database both name the catalog. method picks the way to authenticate; leave it
out to connect as user with no credential:
method |
Fields |
|---|---|
| none (default) | user |
ldap |
user, password, impersonation_user (optional) |
jwt |
jwt_token |
certificate |
client_certificate, client_private_key |
oauth, oauth_console |
none; signs in through a browser or a printed link |
kerberos, gssapi |
principal, krb5_config, service_name (all optional) |
Every authenticated method connects over HTTPS. The server certificate is verified
unless you set cert: false; cert also takes the path to a CA bundle. Kerberos
reads its keytab from the KRB5_CLIENT_KTNAME environment variable, so keytab:
on the source is an error.
max_query_duration_seconds is sent as the query_max_run_time session property.
If session_properties sets query_max_run_time itself, the shorter of the two
applies.
Column types come from Trino's own result metadata. array, map and row
values arrive as JSON text. A type dbt Charts does not read (an interval, a
time with time zone, a sketch type) fails the query with a message naming the
column; CAST it in the query, for example to varchar.
Through profiles.yml. A type: trino target works with type: dbt_profile
and takes the same keys as the direct source, plus dbt's method: none for no
authentication. threads: governs dbt run, so
dbt Charts reads past it. Unlike other targets, dbt Charts validates this one
itself, so an unknown key is an error. target_path: on the dbt_profile source
is not supported for Trino and raises.
If your dbt project's own target is Trino, a query that uses {{ ref() }} with no
source: fails with ERR-DBT-TARGET-NEEDS-SOURCE. Declare a dbt_profile source
for the target and set source: on the query.
Lake compute¶
Lake compute has no dbt Core adapter. dbt Charts connects through the same ADBC
driver dbt v2 uses, which is why the source type is spelled the way dbt v2 spells
it: lakecompute. lake_compute and alt are not accepted.
Install the extra (pip install "dbt-charts[lakecompute]"). The driver itself is
downloaded from dbt's CDN the first time a query runs, checked against a pinned
SHA-256, and cached where dbt v2 keeps its own. A machine that has already run dbt
v2 downloads nothing.
# dbt_charts.yml sources: lake: type: lakecompute token: "{{ env_var('DBT_COMPUTE_AUTH_TOKEN') }}"
The field names are dbt v2's profile keys, flat. method picks the way to
authenticate, and each method takes only its own fields:
method |
Fields |
|---|---|
token (default) |
token |
api_key |
api_key |
okta_browser |
okta_auth_url, okta_token_url, okta_client_id (all optional) |
fivetran |
fivetran_credential, fivetran_auth_url (optional) |
Every method also accepts base_url and catalog_bundle, both optional:
# dbt_charts.yml sources: lake: type: lakecompute method: fivetran fivetran_credential: "{{ env_var('FIVETRAN_PAT') }}" base_url: https://api.lake-compute.fivetran.com/ catalog_bundle: analytics
A field from another method (token next to api_key), database:, schema:,
attribution:, or any key not listed above is an error rather than being ignored.
base_url defaults to the production endpoint, set in dbt_charts.yml as
execution.lakecompute.default_base_url. dbt v2 also reads DBT_COMPUTE_BASE_URL
for that default; dbt Charts does not read environment variables itself. If you
point dbt v2 at another endpoint through that variable, write
base_url: "{{ env_var('DBT_COMPUTE_BASE_URL') }}" so both
agree.
Lake compute runs DuckDB, so write SQL as DuckDB SQL (dialect: lakecompute on a
query names it). max_query_duration_seconds is sent to the driver as its own
per-query timeout. If the backend attaches a warning to a result, such as an export
limit that cut the rows short, the query fails with ERR-WAREHOUSE-RESULT-WARNING
instead of rendering a partial result.
Through profiles.yml. A type: lakecompute target works with
type: dbt_profile, without dbt Core in the picture. The target takes the same
keys as the direct source above (method, token, base_url, and so on), so a
target named lake in the analytics profile is used like this:
# dbt_charts.yml sources: lake: type: dbt_profile profile: analytics target: lake
database:, schema:, threads:, snowflake_pat_duration_days: and
organization: in the target govern what dbt run writes or how dbt itself
authenticates, so dbt Charts reads past them. path: selects dbt's local DuckDB
engine and is rejected. A target written as a list of connections, or with a
lakecompute: sibling key, is dbt v2's multi-connection shape, which dbt Charts
does not read yet. attribution: and target_path: on the dbt_profile source
are not supported for lake compute yet and raise.
If your dbt project's own target is lake compute, a query that uses {{ ref() }}
with no source: fails with ERR-DBT-TARGET-NEEDS-SOURCE. Declare a source like
the one above and set source: on the query.
Files and APIs¶
CSV, Parquet, JSON, and HTTP sources do not need dbt. They are simpler local dbt Charts source definitions for examples, prototypes, and lightweight external data.
CSV¶
A CSV source is a namespace of relations. Each key under files: is a table
name; the value is the path to the CSV file relative to the project root.
# dbt_charts.yml sources: sales_csv: type: csv files: sales: assets/data/sales.csv
Then query it by name:
queries: sales: source: sales_csv sql: SELECT * FROM sales
A single source can expose multiple CSV files as separate tables:
# dbt_charts.yml sources: sales_data: type: csv files: orders: assets/data/orders.csv returns: assets/data/returns.csv
JSON¶
# dbt_charts.yml sources: product_json: type: json files: products: assets/data/products.json
queries: products: source: product_json sql: SELECT * FROM products
Parquet¶
# dbt_charts.yml sources: events: type: parquet files: events: assets/data/events.parquet
queries: event_counts: source: events sql: SELECT * FROM events
Guardrails¶
Two limits bound a single file source's growth: execution.file_source_max_tables
caps the number of table entries a single files: map may declare (default 500),
and execution.file_source_max_bytes caps each table's uncompressed byte size
(default 5 GB), summed across all files matched by its path or glob. Every
format is measured the same way: the file's own bytes for CSV and JSON, and
for Parquet the uncompressed total recorded in the file's footer, so choosing
the compact format never costs you budget. Both are hard errors, not
truncation. Narrow the files: map, raise the limit in dbt_charts.yml, or
use a database connection for data at that scale. A hosted deployment may pin
its own ceiling, and a project's setting can only lower it, never raise it;
where a ceiling is what fired, the limit below is not the knob that moves:
# dbt_charts.yml execution: file_source_max_tables: 100 file_source_max_bytes: 1000000000 # 1 GB
Exploring a file source¶
dct query runs ad-hoc SQL against a file source directly, the same way it
does against a warehouse source:
DuckDB has to materialize the source's files (read and parse them) before it
can answer, and the materialized tables land in an in-memory cache that dies
with the process. A following dct query invocation — a fresh process —
gets no benefit from it and re-reads the files from scratch. The warmth only
pays off inside one long-lived process that runs multiple queries against the
source, such as dct serve, a single render, or one dct validate --warehouse
sweep.
HTTP¶
Unlike SQL and file queries, an HTTP query is self-contained: set url: (and
optional headers:, method:, body:) directly on the query. It has no
source: field; there is no named registry entry to reference.
Runs under local dct only; Not supported on dbtcharts.com.
queries: forecast: type: http url: "https://api.example.com/forecast"
See Queries for the full HTTP query field reference.
Inline File Sources¶
A single CSV, JSON, or Parquet file that only one query reads doesn't need a
named registry entry; reference the file directly on source:, as a path
containing / or ending in a data-file extension:
queries: sales: source: ./data/sales.csv sql: SELECT * FROM sales
The path resolves against the board file's own directory or the project root, whichever
exists (both existing at once is a compile error naming both). The table name is
the file's stem (sales.csv → table sales); a stem that isn't a valid SQL
identifier is a compile error rather than a silently sanitized name; rename
the file or use the named-registry form below. Only a single file is allowed
inline (no files: list); an inline file source is always materialized
straight from the file's git content.
Use the named-registry form (type: csv/json/parquet with a files: map
in dbt_charts.yml, above) instead when a file is shared across boards or a
source exposes more than one file as separate tables.
Source Defaults¶
Set source: on a board to make all queries in that board use a default source
by name. A charts/meta.yml (or a directory's own meta.yml) can set the
same field to default every board beneath it:
source: finance queries: monthly_revenue: sql: SELECT month, revenue FROM finance_revenue sales_pipeline: source: sales sql: SELECT stage, amount FROM sales_pipeline
Sources are resolved from most specific to broadest:
- Query-level
source: - Board-level
source: - The nearest ancestor
source:in thecharts/meta.ymlcascade
An unknown source name is a validation error.
Attribution¶
Every query dbt Charts sends carries attribution so your warehouse's cost tracking can
tell a dbt Charts query apart from ad-hoc human querying. On BigQuery it arrives as
job labels,
queryable as a real column in INFORMATION_SCHEMA.JOBS; on other warehouses it
arrives as a leading JSON comment on the query text.
dbt Charts sets the engine half itself; you don't configure it:
| Label | Meaning |
|---|---|
app |
Always dbt-charts. The one key to filter on; dbt emits app=dbt under the same key, so one predicate covers both. |
dbt_charts_version |
The dct version that sent the query. |
dbt_charts_surface |
Which entry point: cli, serve, mcp (the local dct mcp serve server), or cloud. |
dbt_charts_board |
The dashboard the query belongs to. |
dbt_charts_query |
The query's name within that dashboard. |
dbt_charts_target |
The dbt target, when the query names one. |
dbt_charts_tool |
The AI tool call that issued the query, such as execute_sql. Absent for queries no tool issued. |
A host that knows who is asking adds three more, opaque per-request labels; no emails or display names, since warehouse query history is broadly readable and long-retained:
| Label | Meaning |
|---|---|
dbt_charts_actor |
Opaque ID of the person or agent who caused the query. |
dbt_charts_client |
Opaque ID of the software that asked: a browser session, an AI copilot turn, a connected MCP client (connector for Cloud's hosted connector). |
dbt_charts_request |
A fresh opaque ID per call, correlating this query to the host's own audit log. |
Only Cloud sets these today; it is the only host with a signed-in principal. The
CLI and dct serve have no notion of "who," so they omit all three rather than
inventing a value.
Like dbt_charts_board/_query/_target/_tool, only BigQuery gets dbt_charts_actor,
_client, and _request as real key/value labels; on every other warehouse they
ride the query comment along with the rest of the per-request payload (see below),
so finding them means matching the comment text, not filtering a column.
Where it lands, per warehouse¶
Only BigQuery stores the whole payload as structured key/value. Elsewhere dbt Charts
fills the warehouse's own identity field so app is a column you can filter, and the
rest of the payload rides the query comment:
| Warehouse | Native field | Carries |
|---|---|---|
| BigQuery | job labels (JOBS.labels) |
everything, as key/value |
| Snowflake | QUERY_TAG |
app, version, surface, as JSON |
| Databricks | query tags | app, version, surface, as JSON |
| Trino | client_tags |
app, version, surface |
| Postgres | application_name |
dbt-charts |
| Redshift, Athena, ClickHouse, DuckDB, SQLite | none | query comment only |
| Lake compute | none | nothing; attribution: is not supported yet |
Setting the native field costs nothing: it travels with the connection, not with each
query. If you have already set query_tag, application_name, or client_tags
yourself, dbt Charts leaves your value alone.
Redshift and Athena are deliberately left out: Redshift's query_group routes WLM
queues and Athena's work_group routes data limits and engine settings, so writing
either could move your queries somewhere else, too high a price for a label.
ClickHouse's log_comment is inert but is a setting, which a readonly = 1 user
cannot send at all, and the query comment already lands in system.query_log.
Your own labels¶
Add ownership labels with attribution: on a source. They apply to every
query against it:
# dbt_charts.yml sources: warehouse: type: bigquery project: my-gcp-project dataset: analytics attribution: team: architecture-analytics cost_center: data-platform
Keys and values must match BigQuery's label rules: keys [a-z][a-z0-9_-]{0,62},
values [a-z0-9_-]{0,63}, on every warehouse, so the same source config behaves
identically everywhere. A value that doesn't match is an error, not a silently
rewritten label. The dbt_charts_ prefix and the app key are reserved for the engine.
env_var() works here like anywhere else in a source:
# dbt_charts.yml sources: warehouse: type: bigquery project: my-gcp-project dataset: analytics attribution: env: "{{ env_var('DCT_ENV', 'dev') }}"
To find your dbt Charts spend on BigQuery:
SELECT creation_time, total_bytes_billed, user_email
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_date = CURRENT_DATE()
AND EXISTS (SELECT 1 FROM UNNEST(labels) l WHERE l.key = 'app' AND l.value = 'dbt-charts')
This works whether the query ran under a service account or an individual developer's credentials.
Environment Variables¶
Use env_var() in source configuration to keep credentials out of YAML files:
# dbt_charts.yml sources: analytics: type: postgres host: "{{ env_var('DB_HOST', 'localhost') }}" user: "{{ env_var('DB_USER') }}" password: "{{ env_var('DB_PASSWORD') }}"
Store credentials in .env or your deployment environment:
Keep credentials out of boards
env_var() is for source configuration. Do not put secrets in board YAML,
query text, or rendered output.
Read-only Posture¶
dbt Charts boards read data. They never intentionally write to your warehouse, but connection-level enforcement depends on the source type.
DuckDB and SQLite are opened with driver-level read-only settings. Every other warehouse should use credentials that can only read data. dbt Charts also applies an in-process SQL allowlist before execution, but SELECT-only credentials are the warehouse-side defense.
| Warehouse | In-process enforcement | Recommended operator posture |
|---|---|---|
| DuckDB | Driver read-only mode | Handled by dbt Charts |
| SQLite | Driver read-only URI mode | Handled by dbt Charts |
| Postgres | SQL allowlist only | SELECT-only role |
| Snowflake | SQL allowlist only | SELECT-only role |
| BigQuery | SQL allowlist only | roles/bigquery.dataViewer or equivalent |
| Redshift | SQL allowlist only | SELECT-only IAM role or database user |
| Lake compute | SQL allowlist only | SELECT-only token or credential |
| dbt profile sources | Depends on adapter | SELECT-only profile credential |
File access from SQL¶
dct serve runs strictly read-only: author SQL cannot reach the filesystem or
network through DuckDB, so functions like read_csv(...), read_json_auto(...),
read_parquet(...), and httpfs are not available in query SQL. Point data at a
file source instead; declare it with type: csv, type: json, or type: parquet
and a files: mapping (see Files and APIs), then query the table
by name. dbt Charts parses the file and loads it for querying without granting SQL
any file access.
Related¶
- Queries for the SQL, values, HTTP, and schema query types (dbt models are queried as plain SQL via
ref(), not a separate query type) - Quick Guide for the shortest syntax overview