Skip to main content

zhao logo

zhao (曌)

Crates.io License

Named for the character Empress Wu Zetian invented for herself: 明 (sun and moon) over 空 (sky) — "illuminating everything below." A free, deterministic breaking-change gate for dbt. zhao reads your dbt project's compiled SQL and tells a reviewer exactly what a pull request changed and which downstream models it actually reaches — before anyone has to trace the DAG by hand.

Changed:
  model model.jaffle_shop.stg_customers:
    - column removed: last_name

Downstream impact:
  model model.jaffle_shop.dim_customers:
    [BREAKING] last_name removed from model model.jaffle_shop.stg_customers breaks reference via last_name (column-removed-with-active-references)

Summary: 1 model(s) changed, 1 column(s) changed, 1 breaking, 0 warning

Impacted models: dim_customers

Why

dbt's own state:modified comparison is syntactic: any compiled-SQL text change counts as "modified," and everything downstream is assumed affected. Teams end up either rebuilding their whole downstream cone on every PR (slow CI), or leaning on a human reviewer to catch a removed column, a narrowed type, or a loosened join by reading SQL — something nobody reliably does across a DAG of any real size.

zhao parses the SQL itself and computes real column-level lineage between two states of your project, classifies each change against a fixed Rule catalog (column removed with an active reference, a column's logic changed, type narrowed, join loosened, column added), and reports the exact models each change actually reaches — never the whole DAG, never a guess. Change one column's logic and only the models that read that column, directly or through a column derived from it, are reported; a model that merely sits downstream of the same parent but reads other columns is not. The analysis itself is entirely local: no LLM, no account, and it never reads or sends your actual data — nothing installed in your warehouse beyond what dbt run already needs. The one place a network call happens is resolving a git-native Baseline (dbt compile/dbt deps, the same as running dbt yourself) — skip that entirely by passing --state with an already-compiled manifest, for a genuinely zero-network-call run.

Install

macOS / Linux — Homebrew:

brew install allenhori/zhao/zhao-cli

Windows — Scoop:

scoop bucket add zhao https://github.com/allenhori/zhao-scoop
scoop install zhao-cli

Python / dbt projects — PyPI, with uv or pip:

uv tool install zhao-cli      # or: pip install zhao-cli

A pre-built binary in a wheel, no Rust toolchain needed. Handy for pinning zhao next to dbt-core in your project's own dependencies (e.g. zhao-cli==X.Y.Z), so local dev and CI get the same version.

Any platform — the install script:

curl -fsSL https://raw.githubusercontent.com/allenhori/zhao-cli/master/scripts/install.sh | sh

Downloads the right pre-built binary for your platform from the releases page — no Rust toolchain needed. Windows without Scoop: grab zhao-x86_64-pc-windows-msvc.zip from the same page. Rust users: cargo install zhao-cli (via crates.io), or cargo install --git https://github.com/allenhori/zhao-cli to build directly off master instead of the last tagged release.

Updating: use the same tool you installed with — brew upgrade zhao-cli, scoop update zhao-cli, uv tool upgrade zhao-cli / pip install --upgrade zhao-cli, or zhao update for the install script / a manual download. zhao update detects a Homebrew-, Scoop- or pip/uv-managed install and points you at the right command instead of touching the binary. Homebrew, Scoop and PyPI only carry stable releases; the nightly build is install-script only.

Two release channels: a tagged stable release (v0.1.0, ...) for anything you depend on, and a rolling nightly build off master, always available at the nightly tag — set ZHAO_VERSION=nightly before running the install script above to track it instead. See RELEASING.md for how the two channels work and how releases are cut.

Quickstart

cd your-dbt-project
dbt compile
zhao check --against main

zhao check finds the merge-base between your branch and main, compiles it with dbt to get a Baseline, diffs it against your current state, and exits non-zero if anything breaking fired. Full walkthrough: Getting started.

Rather have your coding agent wire it into your CI (GitHub Actions, Buildkite, Jenkins, GitLab, ...) and zhao.yml? Tell it: "Read https://raw.githubusercontent.com/allenhori/zhao-cli/master/docs/ai-setup.md and set up zhao for this repository." See Set up with an AI agent.

Commands

Command What it does
zhao check The CI gate — diffs against a Baseline, fails on a breaking change.
zhao diff Same engine, always exits 0 — for local inspection during development.
zhao lineage What's upstream/downstream of a model or column, right now (no diff, no git).
zhao update Replaces the current binary with a release from GitHub Releases (for Homebrew, Scoop and pip/uv installs it tells you to use that tool's upgrade command instead). The only command that reaches the network at all — and only to download the binary itself, never to send anything from your project. See What it doesn't do.

Full flag reference: docs/commands.md.

Addons

zhao <name> falls through to a zhao-<name> binary on PATH when <name> isn't one of the built-ins above — the same convention git uses for git <custom-command>. zhao-cli has no compiled-in knowledge of any specific Addon; it only knows the naming convention and forwards arguments, exit code, and output verbatim.

zhao-dbt-plan (a dbt microbatch cascading time-window planner, AGPLv3, separate repo) is the first real Addon. See examples/hello-zhao-addon/ for a minimal reference implementation of the Addon contract if you want to build your own — its README.md is a walkthrough of the whole discovery/input/output contract.

See it, don't just read about it

zhao lineage exports an interactive, self-contained lineage graph by default — click a model or column to trace exactly what it depends on and what depends on it, search, filter, all offline in one HTML file.

Clicking a model, expanding columns, then tracing a calculated column's real upstream source

Open the live demo to try it yourself (rendered via htmlpreview.github.io, since GitHub shows raw HTML as source rather than rendering it — the file itself is also there to download and open locally). More in docs/lineage-html.md.

Configuration

An optional zhao.yml at your project root lets your team set its own severity policy — versioned in the repo, not hidden in a CI script:

preset: strict
rules:
  column-added: pass
defer:
  target: prod
  state: artifacts/prod/manifest.json

Full reference, including monorepo cascading: docs/configuration.md.

CI integration

- uses: actions/checkout@v7
  with: { fetch-depth: 0 }   # zhao's Baseline resolution needs full history
- run: dbt compile
- run: curl -fsSL https://raw.githubusercontent.com/allenhori/zhao-cli/master/scripts/install.sh | sh
- run: PATH="$HOME/.zhao/bin:$PATH" zhao check --against origin/${{ github.base_ref }}

Full example and notes: docs/ci-integration.md.

Documentation

What it doesn't do

zhao never connects to, stores, or holds credentials for your warehouse or database. zhao check/zhao diff/zhao lineage read only the compiled manifest.json dbt itself already produced, entirely on your own machine or CI runner — no secret or token ever passes through zhao to get there. The one place a live connection is genuinely useful (--check-relations, fully optional) still doesn't change that: zhao never opens the connection itself, it hands the check to dbt run-operation and borrows whatever connection your own dbt profile already has.

zhao never reads, collects, or stores your actual data — no row values, nothing about what's in your tables. It only derives structural metadata (schema, lineage, what changed), and that metadata stays on your own filesystem, under target/zhao/, unless you decide otherwise. Nothing is ever sent anywhere automatically: zhao-cli itself makes no network call of its own except zhao update, which only downloads a release binary — it doesn't send anything from your project. The one exception worth naming plainly: resolving a git-native Baseline (the default zhao check --against <ref>, without --state) runs dbt compile/dbt deps as a subprocess — the same commands you'd run yourself, and exactly as network-dependent as they already are for you (package downloads, and often a live warehouse connection depending on your adapter). Pass --state with an already-compiled manifest to skip that path entirely. Nothing about your project is ever sent to zhao or any third party as a side effect of check/diff/lineage — the metadata they write stays under target/zhao/, yours to do whatever you want with, including nothing at all.

zhao also never generates or applies schema-evolution DDL for you: it detects that a change needs manual evolution or a backfill; the decision and the mechanism stay entirely yours.

Status

Early, real-world usable. dbt is the first supported project format, not the definition of what zhao is — the core engine (zhao-core) has no dbt-specific vocabulary baked in, so a second Transformation Tool Adapter is a matter of implementing a trait, not rewriting the engine.

Building from source

cargo build --workspace
cargo test --workspace

Requires a recent stable Rust toolchain (edition 2024). Before opening a PR, also run:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings

See ARCHITECTURE.md for how the code is organized.

License

Apache 2.0.

Release files for zhao-cli 0.5.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for zhao-cli 0.5.6
File
zhao_cli-0.5.6-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
zhao_cli-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
zhao_cli-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
zhao_cli-0.5.6-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
zhao_cli-0.5.6-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 26.2 MB

Release files / zhao_cli-0.5.6-py3-none-win_amd64.whl

Download URL zhao_cli-0.5.6-py3-none-win_amd64.whl
Size 5.5 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
951a65b2b4b9ab6b1dfa9ab43022f5ea625febb25d589efb786d1ee726c025a2
BLAKE2b-256 checksum
How to use checksums
ac15aebf59f5b02da98634faab89bcde92a4b3dea3f36f230a0948f85d89f6cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / zhao_cli-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL zhao_cli-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 5.7 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
39086fccbdf728f1088a9594f14e99bffef685745b6b054b5d162055ee2dfa3d
BLAKE2b-256 checksum
How to use checksums
9c5aa6e23a2066e12a571ff45c1ddb866fc1bc54a20e0d747d2307c87e185c6e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / zhao_cli-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL zhao_cli-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.4 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
e08f5d87c975b47cc005deafa339bff52a5910cba3c7b50a17c399acf530cc10
BLAKE2b-256 checksum
How to use checksums
3b2df3b8768d7fd70cbb9e8dea1bb4d91f7e7635ef0dffa7b248655684bb8403
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / zhao_cli-0.5.6-py3-none-macosx_11_0_arm64.whl

Download URL zhao_cli-0.5.6-py3-none-macosx_11_0_arm64.whl
Size 5.1 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
5e43d8919eff5e6084ae1920d7d1def4c6a1048f71e87a5e5d943350ce9a072e
BLAKE2b-256 checksum
How to use checksums
ced5e092da9c41c3a9019b95e60ef8b20633a3e26a921d5a6b784b324cad61bf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / zhao_cli-0.5.6-py3-none-macosx_10_12_x86_64.whl

Download URL zhao_cli-0.5.6-py3-none-macosx_10_12_x86_64.whl
Size 5.4 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
28f0769094e3ffa6e935145f20f655bc53802322e5eadb1b22c1fd69c44b7b2f
BLAKE2b-256 checksum
How to use checksums
cfc6a3ade230cb7ea12f8148e3db9027374cadbcca62625bb88ae8354cdc9de1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.6 This release

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page