Governing a dbt Estate Without Touching It
Our dbt integration never asks for a warehouse credential, a dbt Cloud token, or write access to your repository. That is not a missing feature — it is the governance design, and every other trust property follows from it.
Governing a dbt Estate Without Touching It
Our dbt integration never asks for a warehouse credential, a dbt Cloud token, or write access to your repository. That is not a missing feature — it is the governance design, and every other trust property follows from it.
Governance tools have a habit of demanding trust before they earn it: connect your warehouse, grant a service account, let the tool write back into your project. We built the dbt integration on the opposite premise. You hand us build artifacts your project already produces — manifest.json from any dbt command, optionally the catalog and semantic manifest — and everything else is posture: what we write, what we refuse to write, what we admit we lost, and what evidence we keep. This article is that posture, stated plainly.
Import is additive — your governed model is never silently rewritten
When you import a dbt estate, models, seeds, snapshots and sources become governed types, columns become elements, accepted_values tests become taxonomies, relationships tests become references, and the dependency map becomes lineage. Run the same import again after your project changes, and here is the rule that matters: existing governed nodes are never mutated or deleted. New datasets and new fields are added; anything that already carries governed meaning is left exactly as it was.
This is deliberate. If re-import could overwrite definitions, then any engineer's refactor — or any agent's automated run — could change what your data means as a side effect. Instead, when the estate and the governed model disagree, that disagreement surfaces through the audit as findings, and closing the gap is a human decision made by someone with the authority to make it. The only thing a re-import refreshes in place is vendor bookkeeping — native type strings, materializations, tags — which is estate metadata, not governed meaning. Import itself requires the Admin role; meaning enters the model through a gate, not a pipe.
Audit is read-only, and even its bookkeeping is opt-in
The audit — the verb your CI calls on every pull request — writes nothing. It parses the artifacts you send, compares them with the governed model, and reports coverage, drift and conformance findings with stable codes and severities. It runs at Viewer role precisely because it never needs more.
We went one step further than "read-only by default." Recording an audit run into the rolling history is something the caller must explicitly request; the audit verb stays strictly read-only unless asked. The one exception is honest by construction: the re-audit verb — which checks whether the last-known estate still conforms after the governed model changes — always records its run, because producing a dated piece of evidence is the entire point of calling it.
Lossiness is a success channel
Most integrations have two outcomes: worked, or failed. Ours has three, and the third is where trust lives. A successful import or audit can still carry a lossiness report: an explicit, itemized account of everything that was approximated or dropped on the way in.
Concrete examples from the dbt connector: a warehouse type like varchar(255) maps to a governed String with the approximation recorded — and the exact native string is preserved in the model's dbt metadata, so nothing is forgotten even when it is simplified. A column that exists only in tests, never documented in the project's YAML, is materialized with a note saying so. When the catalog and the manifest disagree about a column's type, we keep both and let the audit show the mismatch rather than silently picking a winner.
The same honesty applies to storage limits. At import we persist a snapshot of the parsed estate so later re-audits need no fresh artifacts. If your estate is too large for the snapshot cap, the import says so — snapshotStored: false, with a lossiness record explaining the consequence — instead of pretending a capability exists that doesn't. Fresh-artifact audits still work; the re-audit shortcut honestly doesn't.
Errors, by contrast, mean only one thing: we could not proceed. We never blur the two.
Drift is evidence, not opinion
Every audit produces machine-checkable facts: error, warning and info counts; findings with stable codes like field-type-drift, enum-narrowed or contract-not-enforced, each naming its exact subject down to the column; a fingerprint of the artifact content that was examined; and a human-readable report suitable for a pull-request comment.
Recorded runs accumulate into a per-project history — when each run happened, what triggered it, and what it found — and the latest run drives a status badge anyone can see. The result is that "has this estate drifted?" is never an argument between people with different memories. It is a trail: this artifact, on this date, produced these findings against that governed model. Drift checking works in both directions — fresh artifacts against the governed model on every PR, and the stored estate against the governed model whenever a definition changes — so neither side can move without leaving evidence.
Generated contracts go through your review, not around it
The loop closes with generation: from the governed model, CoreModels emits a dbt contracts file — enforced model contracts with per-column data types, not-null constraints, unique tests, accepted values from governed taxonomies and relationships from governed references. And here the posture holds one last time: we never push that file anywhere. Generation is read-only on our side; the output is a plain YAML artifact that enters your repository the same way any change does — as a pull request your engineers review, with a header that says regenerate rather than hand-edit. dbt enforces the result natively. Meaning changes once, upstream, deliberately; enforcement follows through your normal review-and-deploy path.
The contract that leaves the building
One more artifact follows the same posture, in the other direction: the contract you publish to consumers who are not dbt users at all. Reverse-ETL jobs, ML pipelines and other teams consume your marts with no schema guarantee — dbt's contracts protect dbt builds, not external readers. From the governed model, CoreModels exports an Open Data Contract Standard (ODCS v3.1.0) document: a versioned, standard-format contract generated from the same definitions your audits enforce. What makes it more than documentation is the trail behind it — most contract tooling stores a contract and hopes; here, every recorded audit run is a dated statement that the estate still matches the meaning the contract was generated from. A consumer asking "does this contract still hold?" gets evidence, not a modification date.
Import behind an admin gate, audits that write nothing, losses declared rather than hidden, drift preserved as dated evidence, and generated artifacts subject to your own review. None of it requires believing us — every property shows up in the responses you get back. Read them critically; they are designed to survive that. The dbt quickstart in the CoreModels docs shows each verb, and what it does and does not touch, end to end.