DriftGuard catches missing environment variables and broken AI output contracts before merge.
See the engineering case study for architecture, validation evidence, scope boundaries, and a resume-ready project summary.
cargo install --git https://github.com/sidsri14/driftguard --locked && driftguard init && driftguard doctor && driftguard checkAfter the package is published to crates.io, the install command becomes:
cargo install driftguard-cli --lockedFor development from this repository:
cargo run -- init
cargo run -- checkdriftguard init
driftguard check
driftguard check --since origin/main
driftguard check --since origin/main --env-scope changed
driftguard check --since origin/main --markdown
driftguard check --json
driftguard doctor
driftguard doctor --json
driftguard install-hookdriftguard init creates:
env_files = [".env.example"]
ignore_dirs = [".git", "node_modules", "target", "dist", "build", ".next", ".venv", "__pycache__"]
source_globs = ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.cjs", "**/*.py", "**/*.rs"]
ignore_env_keys = []Add prompt contracts as needed:
[prompts.router]
files = ["src/prompts/router.md"]
schema = "schemas/router.schema.json"
golden = "tests/golden/router/*.json"driftguard init starts with no prompt mappings. Add a contract block like the
router example when the project has a prompt output schema. Prompt contracts
are active only when their mapped prompt files exist.
Use --env-scope changed with --since when you only want environment checks
against changed source files. The default --env-scope all scans the configured
source globs across the repository.
Ignore a known non-deployment key across the project:
ignore_env_keys = ["LOCAL_DEV_ONLY"]Suppress a single source line or the next line when generated or dynamic code cannot be represented in an env manifest:
const local = process.env.LOCAL_DEV_ONLY; // driftguard-ignore
// driftguard-ignore-next-line
const generated = process.env.GENERATED_KEY;Keep suppressions narrow and reviewable. Commented-out code is ignored automatically for supported JS/TS, Rust, and Python files.
driftguard check --json writes a versioned JSON document to stdout and keeps
the same exit codes as terminal output:
0: contracts passed1: drift was detected2: DriftGuard could not execute the check
The top-level format_version is currently 1. CI integrations should check
that value before consuming environment or prompts failures.
Execution failures use verdict: "error" and include an error string, so
stdout remains valid JSON for exit code 2.
- JS/TS:
process.env.KEYandprocess.env["KEY"] - JS/TS destructuring:
const { KEY } = process.env - Python:
os.getenv("KEY")andos.environ["KEY"] - Rust:
std::env::var("KEY")andenv!("KEY") - JS/TS/Rust line and block comments are ignored during env scanning
- Python line comments are ignored during env scanning
- Missing keys in configured env manifests
- Prompt golden fixtures that violate configured JSON schemas
- Prompt template variables like
{{user_payload}}missing from golden fixture inputs - Changed prompt markdown files without mapped contracts when
--sinceis used - Project-wide and line-level env scan suppressions
- Configuration health through
driftguard doctor
Simple golden fixtures can be the expected output JSON directly:
{
"destination": "support"
}For templated prompts, use an input object plus an output object. DriftGuard
checks that every {{variable}} in the prompt has a matching input key, then
validates output against the configured JSON Schema:
{
"input": {
"user_payload": "I need help with billing"
},
"output": {
"destination": "support"
}
}The included .github/workflows/driftguard.yml validates this repository by
installing the local crate with cargo install --path .. After DriftGuard is
published, consumer repositories can replace that install step with
cargo install driftguard-cli --locked.
When using driftguard check --since origin/main, keep fetch-depth: 0 on
actions/checkout@v4. DriftGuard needs the base branch ref available locally to
compute changed prompt files.
The repo also includes:
.github/workflows/driftguard.ymlfor normal CI validation.github/workflows/driftguard-pr-comment.ymlfor posting/updating PR comments.github/workflows/quality.ymlfor Linux, macOS, and Windows tests.github/workflows/release.ymlfor tag-based release binaries
See examples/broken-ai-app for a compact app that demonstrates:
- missing environment variable detection
- prompt template input coverage
- prompt output JSON Schema validation
Run the complete pass/fail demo from the repository root:
.\scripts\demo.ps1./scripts/demo.shPlay the recorded 30-second terminal session with asciinema:
asciinema play docs/demo.castRun the scanner benchmark locally:
cargo bench --bench env_scanThe benchmark generates 500 TypeScript files and measures the same scanner used by the CLI. Performance depends on the machine and filesystem, so DriftGuard does not claim a fixed scan time.
DriftGuard validates deterministic contracts. It does not predict LLM quality, execute prompts against model providers, inspect secret values, or replace a language-specific compiler or security scanner.