LEAST-PRIVILEGE PREREQUISITES

This is the page to hand your DBA before granting DVT anything. It answers one question per engine — what is the smallest set of privileges that makes this work? — and it answers it four times, because a read-only federation source and a default target are not the same ask, and neither is the write surface you may never turn on.

Everything below is grounded in what DVT actually issues — the catalog queries source discovery runs, the SQL the load lane sends, the drops retract performs — or in the engine's own published permission model. Where we could verify the exact statement, it is here as a statement you can paste. Where we could not, the cell says unverified and explains what specifically is unproven, rather than inventing grant syntax that reads authoritative and is wrong.

THE FOUR TIERS

READ-ONLY FEDERATION

Introspect the catalog and extract rows. This is what a federation source needs and nothing else — DVT writes nothing to a source engine.

DEFAULT TARGET

Create, load, replace and drop the tables DVT lands. Everything here happens inside one schema you nominate.

DML SURFACE

Hooks, dvt exec, the Explorer grid editor and the api-portal write endpoints. Opt-in, and the one tier where the credential is the only limit.

CLOUD STAGING

What the load lane needs on the engine's object area. Only cloud warehouses and buckets have anything in this column, and on the warehouses it is a recommendation DVT degrades around — not a wall.

They stack in the obvious way. An engine you only read from needs the first column and nothing else. An engine that is also your default target needs the second. The third is opt-in — turn it on and the credential itself becomes the only limit, which is the whole reason it has its own column.

WHAT DVT NEVER NEEDS

The list below is not a policy promise; it is a description of the commands DVT contains. None of these appear in any lane:

  • superuser, sysadmin, ACCOUNTADMIN, db_owner, DBA, metastore adminno lane needs an administrative role on any engine. Every grant on this page is object- or schema-scoped.
  • CREATE DATABASE, DROP DATABASE, DROP SCHEMADVT issues none of them, anywhere. Retract drops the tables and views it recorded as its own and stops there — a project leaves no footprint, but it also never takes a schema with it.
  • any write against a federation sourcethe extraction lane emits a single SELECT of the pruned columns, with pushed-down predicates. No temp tables, no watermark tables, no bookkeeping on the source. Incremental state lives in the local DuckDB cache inside your project.
  • STORAGE CREDENTIAL or EXTERNAL LOCATION on Unity Catalog, storage integrations or external stages on Snowflakethe staging area DVT uses is a managed volume or an internal stage. The highest-privilege objects on both platforms stay out of the conversation.
  • account-, project- or organisation-level IAMon the cloud platforms every role on this page binds to one dataset, one bucket or one prefix.
  • SELECT ANY TABLE and its equivalentssource discovery reads ALL_TABLES on Oracle, information_schema elsewhere — never DBA_* views, never SNOWFLAKE.ACCOUNT_USAGE, never Databricks system tables. Per-object grants are enough because nothing reads above them.

One honest asterisk: DVT issues no create schema, but the dbt lane underneath it will create the target schema if it is missing. Pre-create that schema yourself and the requirement disappears entirely — which is the recommended setup anyway, since it is the schema you are scoping every other grant to.

THE MATRIX

One row per connector, one column per tier. The detail — the actual statements — is in the per-engine sections below.

ENGINESREADTARGETDMLSTAGING
PostgreSQLCONNECT + USAGE + SELECT CREATE on schema + owns what it creates INSERT / UPDATE / DELETE on the target rows none
SnowflakeUSAGE on warehouse + db + schema, SELECT CREATE TABLE / CREATE VIEW on the schema INSERT / UPDATE / DELETE / TRUNCATE CREATE STAGE on the schema — recommended NEXT RELEASE
BigQuerydataViewer on the dataset + jobUser dataEditor on the target dataset + jobUser dataEditor (DML is table-data writes) unverified — DVT configures no staging bucket
RedshiftUSAGE on schema + SELECT CREATE on schema, DROP + TRUNCATE on tables INSERT / UPDATE / DELETE none — no S3 COPY on this lane
DatabricksUSE CATALOG + USE SCHEMA + SELECT CREATE TABLE + MODIFY on the schema MODIFY CREATE VOLUME on the schema — recommended NEXT RELEASE
MySQLSELECT on the database CREATE / DROP / ALTER + write on the database INSERT / UPDATE / DELETE none
MariaDBSELECT on the database CREATE / DROP / ALTER + write on the database INSERT / UPDATE / DELETE none
SQL Serverdb_datareader CREATE TABLE + ALTER on the schema INSERT / UPDATE / DELETE none
Microsoft Fabricread on the warehouse + SELECT CREATE TABLE + ALTER on the schema INSERT / UPDATE / DELETE none configured
OracleCREATE SESSION + SELECT CREATE TABLE / VIEW + tablespace quota INSERT / UPDATE / DELETE none
DuckDBfilesystem read on the .duckdb file filesystem write on the file and its directory filesystem write none
ClickHouseSELECT + SHOW on the database CREATE TABLE / DROP TABLE / INSERT / ALTER INSERT + ALTER UPDATE / ALTER DELETE none
Trinoaccess-control rules, not GRANT write rules on one catalog connector-dependent none from DVT
SQLitefilesystem read on the .db file filesystem write on the file and its directory filesystem write none
AthenaIAM: athena + glue read + S3 read IAM: Glue write + S3 write on the data prefix S3 write on the data prefix S3 staging location, read + write
BUCKETS & FILE LANESREADTARGETDMLSTAGING
Amazon S3s3:ListBucket + s3:GetObject + s3:PutObject not applicable + s3:DeleteObject, for retract only
Google Cloud Storageroles/storage.objectViewer on the bucket + roles/storage.objectCreator not applicable delete rights, for retract only
Azure Blob StorageStorage Blob Data Reader Storage Blob Data Contributor not applicable delete rights, for retract only
SFTPread + traverse on the path write on the target directory not applicable no delete needed
Local FilesystemOS read on the path OS write on the directory not applicable OS delete, for retract only

verified — the statement was read from the code path that issues it and the privilege from the engine's own reference. unverified — one half is unproven; the engine's section says which. Blank cells mean the tier does not exist for that connector. NEXT RELEASEnext release — the cell describes behaviour that is not in 0.2.40; the engine's section says what 0.2.40 does instead.

CLOUD STAGING: THE TWO HALVES, AND THE HONEST DEGRADATION

NEXT RELEASE — NOT IN 0.2.40

What follows is built and documented ahead of the release that carries it. pip install dvt-core gives you 0.2.40, and 0.2.40 does not do it yet.

On 0.2.40, today: the per-run staging root is the part that has not shipped. The published load lane issues no CREATE STAGE and no CREATE VOLUME — on Snowflake and Databricks alike the load stages into the loader's own default area, which is precisely the degraded path this section describes. So the honest reading for a DBA scoping a role today is: skip the DDL grant, grant the file half, and expect staging residue to live in that shared area rather than leave with the run. Everything else on this page — READ, TARGET and DML — is shipped behaviour and stands as written.

On Snowflake and Databricks the load travels through the engine's own object area — an internal stage, a Unity Catalog volume. Given the grant, DVT mints its own staging root, per run, and drops it wholesale when the run ends. That is what makes cleanup a single DROP instead of a hunt through a shared directory that another session might be using.

Two things about this column matter more than the grant syntax, and both are places DBAs get caught:

  • the DDL grant and the file permission are different grantsbeing allowed to create a stage or a volume is not being allowed to write files into it. On Databricks the file cleanup does not even go through SQL — it calls the workspace Files API with the connection's token, so that token needs file write and delete on the volume on top of the Unity Catalog grant. A principal with immaculate SQL grants and no file access fails halfway through a load.
  • these grants are recommended, not requiredwithhold them and the load still runs. DVT logs the missing grant by name, once, and falls back to the loader's shared default staging root. What you lose is the wholesale drop: staging residue then accumulates in that shared location instead of leaving with the run. Nothing fails, nothing is silent, and the choice stays yours.

Verification status:the two staging cells are read from the loader's source and from each platform's own privilege reference — they are not measured against a live warehouse under exactly that grant set. They are marked unverified in the matrix for that reason, and they will stay marked until a run proves them. Everything else in the staging column is a plain statement that there is no staging area at all.

THE DML SURFACE, STATED PLAINLY

Four features write rows: pre- and post-hooks on federated models, dvt exec, the Explorer grid editor, and the API portal's write endpoints. They share one execution path, and the honest description of that path is: it sends the statement as written. DVT does not parse it, narrow it, or wrap it in a transaction.

Which leads to the single most important sentence on this page: if least privilege matters to you, enforce it at the engine — a hook may name any connection in your profile, including one you think of as a read-only source. A source credential that is read-only at the engine cannot be talked into a write by anything in DVT. A source credential that happens to be an admin account can.

The guardrails that do exist, stated exactly:

  • the Explorer's raw SQL box is SELECT-only, enforced by parsingnot by convention. Reads also get an engine-side row limit injected.
  • the grid editor refuses keyless updates and deletesan edit with no key would touch every row, so it is rejected server-side rather than executed. Applies run one statement at a time and stop at the first failure — there is no cross-engine transaction to roll back into.
  • API-portal writes are off until you turn them onand they need key enforcement enabled, a valid key, the model enabled, a separate per-model writes toggle, an allow-listed column set and a non-empty where clause. Every executed statement is written to an audit log inside the project.
  • buckets and file connections refuse the DML lane outrighta bucket is rewritten, never updated in place. Hooks, exec and the write endpoints cannot address one.

PER-ENGINE DETAIL

Jump to yours. Each block gives the four tiers, with statements where the syntax is verified and plain English where it is not.

READ-ONLY FEDERATION

Source discovery reads information_schema.tables; extraction is a plain SELECT of the pruned columns. information_schema is readable by every role and already filters itself to objects the role can see, so no extra metadata grant is needed.

GRANT CONNECT ON DATABASE analytics TO dvt_reader;
GRANT USAGE ON SCHEMA public TO dvt_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO dvt_reader;

-- so tomorrow's tables are covered too
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO dvt_reader;

DEFAULT TARGET

Sling creates and loads the landed table; DVT sends a `drop table if exists … cascade` before a full refresh and `drop table` / `drop view` on retract. CREATE on the schema is enough because the role then owns everything DVT made — dropping is an ownership right, not a grant. TRUNCATE is what a plain `dvt seed` uses.

GRANT CONNECT ON DATABASE analytics TO dvt_target;
GRANT USAGE, CREATE ON SCHEMA analytics TO dvt_target;

-- only if DVT must load into tables it did not create
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE
  ON ALL TABLES IN SCHEMA analytics TO dvt_target;

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE ON analytics.orders TO dvt_target;

CLOUD STAGING

There is no warehouse-side staging area. Rows land through the connection itself; the only staging DVT owns is local Parquet under the project's .dvt/tmp.

  • The full-refresh pre-drop is CASCADE. It will take down dependent views the role owns — give DVT its own schema if that matters.

READ-ONLY FEDERATION

Discovery reads information_schema.tables — never SHOW, never SNOWFLAKE.ACCOUNT_USAGE, so no ACCOUNTADMIN-adjacent grant appears. A warehouse is a required connection field even for pure reads, which makes USAGE ON WAREHOUSE a genuine read-tier prerequisite.

GRANT USAGE ON WAREHOUSE dvt_wh TO ROLE dvt_reader;
GRANT USAGE ON DATABASE analytics TO ROLE dvt_reader;
GRANT USAGE ON SCHEMA analytics.raw TO ROLE dvt_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA analytics.raw TO ROLE dvt_reader;
GRANT SELECT ON FUTURE TABLES IN SCHEMA analytics.raw TO ROLE dvt_reader;

DEFAULT TARGET

The role creates the landed table and therefore owns it, which covers the insert, the replace and the retract drop. Nothing at database or account level is required beyond USAGE.

GRANT USAGE ON WAREHOUSE dvt_wh TO ROLE dvt_target;
GRANT USAGE ON DATABASE analytics TO ROLE dvt_target;
GRANT USAGE, CREATE TABLE, CREATE VIEW
  ON SCHEMA analytics.marts TO ROLE dvt_target;

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE, TRUNCATE
  ON TABLE analytics.marts.orders TO ROLE dvt_target;

CLOUD STAGING

RECOMMENDED, NOT REQUIREDUNVERIFIEDNEXT RELEASE

Bulk loads travel through a Snowflake internal stage. Given CREATE STAGE on the schema, DVT mints its own per-run stage — `create stage if not exists <db>.<schema>.dvt_staging_<12 hex>` — and drops it wholesale when the run ends, so the staging tree is DVT's by construction and leaves nothing behind. Reading and writing the files in that stage is a separate permission from the DDL grant: a role that may create a stage but not write to it will fail at the first file.

-- let DVT mint and drop its own per-run stage
GRANT CREATE STAGE ON SCHEMA analytics.marts TO ROLE dvt_target;

-- if CREATE STAGE is off the table, point DVT at one you own:
GRANT USAGE ON STAGE analytics.marts.dvt_staging TO ROLE dvt_target;
GRANT READ, WRITE ON STAGE analytics.marts.dvt_staging TO ROLE dvt_target;

Without it: The load still runs. DVT logs the missing grant by name, once, and falls back to the loader's shared default stage — which it cannot drop wholesale. Staging residue then accumulates in that shared location instead of disappearing with the run.

Unverified: Read from the loader's source, not measured against a live Snowflake account. The privilege names are Snowflake's own and are verified; what is unproven here is the end-to-end run under exactly this grant set.

NEXT RELEASE — NOT IN 0.2.40

What follows is built and documented ahead of the release that carries it. pip install dvt-core gives you 0.2.40, and 0.2.40 does not do it yet.

On 0.2.40, today: DVT mints no stage. Nothing in the published load lane issues CREATE STAGE, so granting it buys you nothing yet — every load already travels through the loader's own default internal stage, which is exactly the fallback described under “Without it”. Grant the READ/WRITE half if you are scoping a role now; the CREATE STAGE half is the one that starts paying off next release.

  • Never ACCOUNTADMIN, never SECURITYADMIN, never an external stage, never a storage integration, never a file format — the per-run stage is an internal one.
  • The fallback path cleans files with a REMOVE against the shared default stage. Giving DVT its own stage is what turns cleanup from per-file into one DROP.

READ-ONLY FEDERATION

Discovery queries the dataset's own INFORMATION_SCHEMA.TABLES, and extraction runs a query — so the identity needs both the data role on the dataset and the right to run jobs in the project.

# dataset-scoped read
gcloud projects add-iam-policy-binding PROJECT \
  --member='serviceAccount:dvt@PROJECT.iam.gserviceaccount.com' \
  --role='roles/bigquery.jobUser'

bq add-iam-policy-binding \
  --member='serviceAccount:dvt@PROJECT.iam.gserviceaccount.com' \
  --role='roles/bigquery.dataViewer' \
  PROJECT:analytics

DEFAULT TARGET

dataEditor covers creating, replacing and deleting the dataset's tables plus writing their data. Nothing at organisation or folder level is involved.

bq add-iam-policy-binding \
  --member='serviceAccount:dvt@PROJECT.iam.gserviceaccount.com' \
  --role='roles/bigquery.dataEditor' \
  PROJECT:marts

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

CLOUD STAGING

UNVERIFIED

DVT never sets a GCS staging location on the BigQuery connection, so the load falls through to whatever the loader does by default. If that turns out to be a GCS-staged load, the identity will also need object create and delete on the bucket it picks.

Unverified: BigQuery is a supported adapter, not one of the engines standing in DVT's live verification suite — so this cell is a reading of the code path, not an observed run. Treat it as: grant the two roles above, run a load, and read the error if there is one.

  • Never roles/bigquery.admin, never a project-level owner or editor role.

READ-ONLY FEDERATION

Discovery reads information_schema.tables — no SVV_* system views, no catalog-admin grant. Redshift's own ACCESS CATALOG system permission is not needed.

GRANT USAGE ON SCHEMA public TO dvt_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO dvt_reader;

DEFAULT TARGET

Same shape as PostgreSQL, with Redshift's explicit DROP privilege: the full-refresh pre-drop is a CASCADE drop and retract drops tables and views.

GRANT USAGE, CREATE ON SCHEMA analytics TO dvt_target;
GRANT SELECT, INSERT, UPDATE, DELETE, DROP, TRUNCATE
  ON ALL TABLES IN SCHEMA analytics TO dvt_target;

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE ON analytics.orders TO dvt_target;

CLOUD STAGING

DVT maps Redshift over the PostgreSQL wire protocol and configures no S3 bucket, so loads do not go through COPY. There is no bucket policy to write and no ASSUMEROLE grant to make — and, honestly, no COPY-speed bulk load either: large loads travel over the wire.

  • IAM authentication is refused on DVT's data-movement lane — this connection takes a database user and password. Short-lived credentials are not available here.
  • The full-refresh pre-drop is CASCADE, same caveat as PostgreSQL.

READ-ONLY FEDERATION

Discovery reads the catalog's own information_schema.tables — never the system.* tables, which is what keeps this off metastore-admin territory. USE CATALOG and USE SCHEMA grant no data by themselves; SELECT does.

GRANT USE CATALOG ON CATALOG main TO `dvt_reader`;
GRANT USE SCHEMA ON SCHEMA main.raw TO `dvt_reader`;
GRANT SELECT ON SCHEMA main.raw TO `dvt_reader`;

DEFAULT TARGET

The loader creates the landed table and merges into it; MODIFY is Unity Catalog's insert/update/delete privilege. Dropping on retract follows from owning what was created.

GRANT USE CATALOG ON CATALOG main TO `dvt_target`;
GRANT USE SCHEMA, CREATE TABLE ON SCHEMA main.marts TO `dvt_target`;
GRANT SELECT, MODIFY ON SCHEMA main.marts TO `dvt_target`;

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT MODIFY ON TABLE main.marts.orders TO `dvt_target`;

CLOUD STAGING

RECOMMENDED, NOT REQUIREDUNVERIFIEDNEXT RELEASE

The load stages files in a Unity Catalog volume and then runs COPY INTO. Given CREATE VOLUME on the schema, DVT mints its own per-run volume — `create volume if not exists <catalog>.<schema>.dvt_staging_<12 hex>` — and drops it when the run ends. Watch the second half: the file cleanup does not go through SQL at all. It calls the workspace Files API with the connection's token, so that token needs write and delete on the volume's files in addition to the SQL grant. This is the trap — a principal with perfect SQL grants and no file access fails halfway through.

-- the SQL half: let DVT mint and drop its own per-run volume
GRANT CREATE VOLUME ON SCHEMA main.marts TO `dvt_target`;

-- the file half, required either way
GRANT READ VOLUME, WRITE VOLUME ON SCHEMA main.marts TO `dvt_target`;

-- if CREATE VOLUME is off the table, scope the file grants to one volume you own
GRANT READ VOLUME, WRITE VOLUME
  ON VOLUME main.marts.dvt_staging TO `dvt_target`;

Without it: The load still runs. DVT logs the missing grant by name, once, and falls back to the loader's shared default volume — which it cannot drop wholesale, so staging residue accumulates there instead of leaving with the run.

Unverified: Read from the loader's source, not measured against a live workspace. The Unity Catalog privilege names are Databricks' own and are verified; the end-to-end run under exactly this grant set is not.

NEXT RELEASE — NOT IN 0.2.40

What follows is built and documented ahead of the release that carries it. pip install dvt-core gives you 0.2.40, and 0.2.40 does not do it yet.

On 0.2.40, today: DVT mints no volume, and calls no Files API. Nothing in the published load lane issues CREATE VOLUME, so that grant buys you nothing yet — every load stages into the loader's own default volume, which is the fallback described under “Without it”. The file half is the half that matters today: READ VOLUME and WRITE VOLUME are what a load actually needs. Grant those now; CREATE VOLUME is the one that starts paying off next release.

  • A managed volume needs no STORAGE CREDENTIAL and no EXTERNAL LOCATION — the two highest-privilege objects in Unity Catalog. DVT never asks for either.
  • DVT's data-movement lane requires a personal access token; OAuth and service-principal browser flows are refused there. Issue the token for a dedicated principal whose grants are exactly the ones above — the token is the boundary, so it should carry nothing else.

READ-ONLY FEDERATION

Discovery reads information_schema.tables, whose rows MySQL already filters down to objects the account has a privilege on — so SELECT is the whole grant.

CREATE USER 'dvt_reader'@'%' IDENTIFIED BY '…';
GRANT SELECT ON analytics.* TO 'dvt_reader'@'%';

DEFAULT TARGET

The loader creates and replaces the landed table; retract drops tables and views. A plain `dvt seed` truncates rather than dropping, which DELETE covers.

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER
  ON analytics.* TO 'dvt_target'@'%';

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE ON analytics.orders TO 'dvt_target'@'%';

CLOUD STAGING

No warehouse-side staging area — rows land through the connection.

  • Never GRANT ALL ON *.*, never SUPER, never the mysql system database.

READ-ONLY FEDERATION

MariaDB rides the MySQL adapter and the MySQL wire protocol, so the grant is identical: SELECT, with information_schema filtering itself.

GRANT SELECT ON analytics.* TO 'dvt_reader'@'%';

DEFAULT TARGET

Same as MySQL.

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER
  ON analytics.* TO 'dvt_target'@'%';

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE ON analytics.orders TO 'dvt_target'@'%';

CLOUD STAGING

No warehouse-side staging area.

READ-ONLY FEDERATION

Discovery reads INFORMATION_SCHEMA.TABLES, and the Explorer's key discovery reads TABLE_CONSTRAINTS and KEY_COLUMN_USAGE. SQL Server shows catalog rows only for objects the principal has a permission on, so db_datareader carries the metadata too.

CREATE USER dvt_reader FOR LOGIN dvt_reader;
ALTER ROLE db_datareader ADD MEMBER dvt_reader;

-- only if your instance has revoked default metadata visibility
GRANT VIEW DEFINITION ON SCHEMA::dbo TO dvt_reader;

DEFAULT TARGET

ALTER on the schema is what lets the account create and drop objects inside it; db_datawriter covers the loaded rows.

ALTER ROLE db_datareader ADD MEMBER dvt_target;
ALTER ROLE db_datawriter ADD MEMBER dvt_target;
GRANT CREATE TABLE TO dvt_target;
GRANT ALTER, SELECT, INSERT, UPDATE, DELETE
  ON SCHEMA::analytics TO dvt_target;

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE ON analytics.orders TO dvt_target;

CLOUD STAGING

No blob staging is configured on this connection — rows land over the connection.

  • Never sysadmin, never db_owner.

MICROSOFT FABRIC

connector guide →

READ-ONLY FEDERATION

UNVERIFIED

Fabric goes through the same T-SQL surface as SQL Server, and DVT runs the same INFORMATION_SCHEMA discovery against it. The identity is an Entra principal with read access to the warehouse plus SELECT on the objects.

-- once the Entra principal has warehouse access
GRANT SELECT ON SCHEMA::dbo TO [dvt-reader@contoso.com];

Unverified: The T-SQL syntax is Fabric's documented surface, but Fabric is a supported adapter that does not stand in DVT's live verification suite — the item-level Fabric permission that must sit underneath the T-SQL grant is workspace configuration we have not exercised.

DEFAULT TARGET

UNVERIFIED

The SQL Server shape, with an Entra principal instead of a SQL login.

GRANT CREATE TABLE TO [dvt-target@contoso.com];
GRANT ALTER, SELECT, INSERT, UPDATE, DELETE
  ON SCHEMA::analytics TO [dvt-target@contoso.com];

Unverified: Same reason as the read tier — not exercised in the verification suite.

DML SURFACE

UNVERIFIED

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

Unverified: Not exercised in the verification suite.

CLOUD STAGING

DVT sets no blob staging location on this connection; loads go over the T-SQL connection like SQL Server.

  • Authentication is Entra-only on this connection — there is no SQL-login path.

READ-ONLY FEDERATION

Discovery reads ALL_TABLES and ALL_USERS; the catalog and the Explorer read ALL_TAB_COLUMNS, ALL_CONSTRAINTS and ALL_CONS_COLUMNS. Those views are granted to PUBLIC on a stock database and show only what the account may already see, so the grant list is just the session and the tables.

GRANT CREATE SESSION TO dvt_reader;
GRANT SELECT ON app.customers TO dvt_reader;
GRANT SELECT ON app.orders    TO dvt_reader;

DEFAULT TARGET

The account creates the landed table in its own schema, so it owns the drop. The quota is the part people forget — without it, creating a segment fails no matter what was granted.

GRANT CREATE SESSION, CREATE TABLE, CREATE VIEW TO dvt_target;
ALTER USER dvt_target QUOTA UNLIMITED ON users;

DML SURFACE

Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, UPDATE, DELETE ON analytics.orders TO dvt_target;

CLOUD STAGING

No staging area — rows land through the connection.

  • Retract issues `drop table … cascade constraints`, and Oracle has no `if exists` there — the statement is only sent for relations DVT recorded as its own.
  • Never DBA, never SELECT ANY TABLE — per-table grants are enough because discovery never reads DBA_* views.

READ-ONLY FEDERATION

DuckDB has no user model. The privilege boundary is the operating system: whoever can read the database file can read every table in it.

DEFAULT TARGET

DVT creates, replaces and drops tables in the file, so the process needs write on the file and on the directory holding it.

DML SURFACE

Same boundary — there is nothing finer to grant. If a DuckDB file must be read-only to DVT, make it read-only to the OS user running DVT.

CLOUD STAGING

DuckDB is where federation compute already happens; there is nothing to stage.

  • DuckDB is single-writer. Two DVT runs writing the same file will contend, and no grant changes that.

READ-ONLY FEDERATION

Discovery reads system.tables and the catalog reads system.columns. ClickHouse filters system-table rows by the account's SHOW privileges, so SELECT plus SHOW is the read tier.

GRANT SELECT ON analytics.* TO dvt_reader;
GRANT SHOW TABLES, SHOW COLUMNS ON analytics.* TO dvt_reader;

DEFAULT TARGET

The loader creates and replaces the landed table; incremental deltas apply as ALTER UPDATE / ALTER DELETE rather than a MERGE, which is why ALTER is on this list.

GRANT SELECT, INSERT, ALTER, CREATE TABLE, DROP TABLE, TRUNCATE
  ON analytics.* TO dvt_target;

DML SURFACE

ClickHouse expresses updates and deletes as mutations, so an Explorer edit or a hook lands as an ALTER. Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

GRANT INSERT, ALTER UPDATE, ALTER DELETE ON analytics.orders TO dvt_target;

CLOUD STAGING

No object-store staging area — rows land through the connection.

READ-ONLY FEDERATION

Trino has no portable GRANT: authorization lives in a system access control plugin — the built-in read-only or file-based rules, Ranger, or OPA — evaluated before any connector-level check. Discovery reads information_schema.tables, so the rules must allow SELECT and the metadata reads on the catalogs and schemas you want federated.

DEFAULT TARGET

To land results, the same rules must permit creating, inserting into and dropping tables in the target catalog and schema. Scope that to one writable catalog and leave the rest read-only.

DML SURFACE

Whether UPDATE and DELETE exist at all is the connector's decision, not a privilege: several Trino connectors accept inserts but reject row-level updates and deletes outright. The Explorer grid editor and `dvt exec` fail loudly on those, whatever the rules say.

CLOUD STAGING

DVT configures no staging area for Trino. Where the underlying connector writes its files, and which object-store credential it uses, is the cluster's configuration.

READ-ONLY FEDERATION

No user model. Discovery reads sqlite_master and the Explorer reads pragma_table_info — both are inside the file, so file permissions are the whole story.

DEFAULT TARGET

SQLite writes a journal alongside the database, so the directory must be writable, not just the file.

DML SURFACE

Same boundary as the target tier.

CLOUD STAGING

Nothing to stage.

  • Single-writer, like DuckDB — concurrent runs against one file contend.

READ-ONLY FEDERATION

UNVERIFIED

Athena is the one engine where DVT passes object-store locations explicitly — the data location and the staging location from the connection. A read identity needs the Athena query actions, Glue Data Catalog reads for the metadata, S3 read on the data prefix, and S3 write on the query-results prefix, because Athena writes every result set there.

Unverified: The three permission families are Athena's documented requirements. The exact action list belongs to your workgroup and Glue setup, and Athena does not stand in DVT's live verification suite — so this page names the families rather than publishing a policy document that would be wrong for half of readers.

DEFAULT TARGET

UNVERIFIED

As a default target Athena creates tables through Glue and writes their files to the data prefix, so the identity needs Glue create/update/delete on that database plus S3 write and delete on the prefix.

Unverified: Same reason — Athena is supported, not verified in the suite.

DML SURFACE

UNVERIFIED

Row-level updates and deletes depend on the table format underneath. Whatever the statement itself requires on the object it touches — on whichever connection the hook, the exec or the write endpoint names. DVT does not narrow it in code.

Unverified: Not exercised in the verification suite.

CLOUD STAGING

The staging location on the connection is a real prefix that Athena writes to on every query. Point it at a prefix DVT owns, with a lifecycle rule, and grant read, write and delete there.

  • Athena is pushdown-only today — it works as a default target, but there is no federation extraction from it. Its S3 data reads as a bucket source in the meantime. See the limitations page.

READ-ONLY FEDERATION

Source discovery is a recursive listing of the prefix, and extraction reads the objects. Scope ListBucket to the prefix with a condition rather than granting it on the whole bucket.

{
  "Effect": "Allow",
  "Action": ["s3:ListBucket"],
  "Resource": "arn:aws:s3:::my-lake",
  "Condition": {"StringLike": {"s3:prefix": ["raw/*"]}}
},
{
  "Effect": "Allow",
  "Action": ["s3:GetObject"],
  "Resource": "arn:aws:s3:::my-lake/raw/*"
}

DEFAULT TARGET

Landing results in a bucket writes objects under the target prefix. Nothing else on the bucket is touched.

{
  "Effect": "Allow",
  "Action": ["s3:PutObject"],
  "Resource": "arn:aws:s3:::my-lake/marts/*"
}

DML SURFACE

DVT refuses the DML lane on buckets and filesystems outright — hooks, `dvt exec`, the grid editor and the write endpoints cannot address a bucket connection. A bucket is rewritten, never updated in place.

CLOUD STAGING

`dvt retract` deletes the objects it landed. If you never run retract against this connection, leave DeleteObject off entirely.

{
  "Effect": "Allow",
  "Action": ["s3:DeleteObject"],
  "Resource": "arn:aws:s3:::my-lake/marts/*"
}

Unverified: One unverified edge: large uploads may go multipart, which also wants s3:AbortMultipartUpload. Add it if a big landing fails on cleanup.

  • MinIO and other S3-compatible stores use this same connector with an endpoint — the policy shape is identical if the store implements IAM policies.

GOOGLE CLOUD STORAGE

connector guide →

READ-ONLY FEDERATION

Listing the prefix and reading the objects — nothing more.

gcloud storage buckets add-iam-policy-binding gs://my-lake \
  --member='serviceAccount:dvt@PROJECT.iam.gserviceaccount.com' \
  --role='roles/storage.objectViewer'

DEFAULT TARGET

Creating objects under the target prefix.

gcloud storage buckets add-iam-policy-binding gs://my-lake \
  --member='serviceAccount:dvt@PROJECT.iam.gserviceaccount.com' \
  --role='roles/storage.objectCreator'

DML SURFACE

The DML lane refuses bucket connections.

CLOUD STAGING

Deleting landed objects needs storage.objects.delete — roles/storage.objectAdmin carries it, or use a custom role if you would rather not grant the rest.

AZURE BLOB STORAGE

connector guide →

READ-ONLY FEDERATION

Listing and reading blobs in the container. The connection takes a storage account key, which is worth reading twice: an account key is an account-wide credential, so container-level scoping is not expressible in the connection itself.

DEFAULT TARGET

Writing blobs under the target prefix, and deleting them on retract.

DML SURFACE

The DML lane refuses bucket connections.

CLOUD STAGING

Contributor covers the delete that retract performs.

  • Because the credential field is an account key, the honest least-privilege answer on Azure is a storage account that holds only what DVT should reach. Scoping a key below the account is not something this connector can express today.

READ-ONLY FEDERATION

Discovery walks the configured root, so the account needs to list directories under it and read the files. Ordinary POSIX permissions, or the server's own chroot.

DEFAULT TARGET

Landing results writes files into the configured path.

DML SURFACE

The DML lane refuses file connections.

CLOUD STAGING

`dvt retract` does not delete SFTP objects — it says so and skips. Files DVT landed on an SFTP target are yours to remove, which also means the account never needs delete permission.

LOCAL FILESYSTEM

connector guide →

READ-ONLY FEDERATION

The privilege model is the operating system's, evaluated as the user running DVT.

DEFAULT TARGET

Landing results writes files; the directory must be writable.

DML SURFACE

The DML lane refuses file connections.

CLOUD STAGING

Retract removes the files it landed.

How this page stays honest: a cell is verifiedwhen both halves check out — the statement DVT issues was read from the code path that issues it, and the privilege that covers it was read from the engine's own permission reference. It is unverifiedwhen either half is missing, most often because the engine is supported rather than standing in DVT's live verification suite. Unverified never means "probably fine": it means the note tells you exactly which half is unproven, so you can close the gap with one test run instead of trusting a docs page. Cells get promoted, never quietly reworded.