Skip to main content

Drift Detection

Drift detection answers the CISO's Tuesday question: "We were safe on Monday. What specifically changed in our configuration that made us unsafe on Tuesday?"

stave diff compares two observation snapshots and reports every asset-level configuration change — resources added, removed, or reconfigured. Configuration drift is treated as a violation (exit code 3), making Stave an infrastructure integrity gate.

How it works

stave diff --snapshot-before monday.json --snapshot-after tuesday.json

Stave loads both snapshots, computes property-level diffs for every asset, and reports changes. The diff engine is deterministic — same inputs always produce the same output.

Change types

TypeMeaning
PROVISIONEDNew resource appeared in current snapshot
DECOMMISSIONEDResource present in baseline is missing from current
RECONFIGUREDExisting resource has property changes

Security-relevant highlighting

In text output, attributes related to security posture are prefixed with [!] to help researchers spot attack-relevant changes:

Changes:
[RECONFIGURED] my-bucket
[!] properties.storage.access.public_read: false → true
[!] properties.storage.encryption.enabled: true → false
properties.storage.tags.team: platform → data-eng

Highlighted attributes include: public, encrypt, logging, audit, mfa, policy, acl, iam, auth, tls, ssl, versioning, backup.

Value redaction

Attributes containing secret, token, or credential have their values automatically masked as [REDACTED] in text output.

Output formats

Text (default)

Drift Report
------------
Period: 2026-01-01T00:00:00Z → 2026-01-11T00:00:00Z

Summary: 2 changed (1 new, 0 removed, 1 reconfigured)

Changes:
[PROVISIONED] new-public-bucket
[RECONFIGURED] my-bucket
[!] properties.storage.access.public_read: false → true

JSON

{
"schema_version": "diff.v0.1",
"kind": "observation_delta",
"start_time": "2026-01-01T00:00:00Z",
"end_time": "2026-01-11T00:00:00Z",
"summary": {
"provisioned": 1,
"decommissioned": 0,
"reconfigured": 1,
"total": 2
},
"changes": [
{
"asset_id": "new-public-bucket",
"action": "PROVISIONED",
"current_type": "aws_s3_bucket"
},
{
"asset_id": "my-bucket",
"action": "RECONFIGURED",
"previous_type": "aws_s3_bucket",
"current_type": "aws_s3_bucket",
"drifts": [
{
"attribute": "properties.storage.access.public_read",
"old_value": false,
"new_value": true
}
]
}
]
}

The JSON schema (diff.v0.1) is designed for SIEM ingestion — flat, stable field names, deterministic ordering.

Exit codes

CodeMeaning
0No drift — baseline and current are identical
2Input error — missing file, invalid JSON, schema mismatch
3Drift detected — one or more resources changed

Exit code 3 matches the violation convention used by stave apply, making stave diff composable in CI/CD pipelines:

stave diff --snapshot-before before-deploy.json --snapshot-after after-deploy.json || exit 1

CI/CD workflow

Post-deploy verification

# Capture baseline before deploy
stave apply --format json > baseline.json

# ... deploy happens ...

# Capture current state after deploy
stave apply --format json > current.json

# Detect drift — exit 3 if anything changed unexpectedly
stave diff --snapshot-before baseline.json --snapshot-after current.json

Scheduled drift monitoring

# Daily: compare yesterday's snapshot to today's
stave diff \
--snapshot-before observations/2026-04-11.json \
--snapshot-after observations/2026-04-12.json \
--format json > drift-report.json

Relationship to other features

FeatureQuestion it answers
stave applyIs the current state safe?
stave diffWhat changed between two points in time?
stave bisectWhen was a violation first introduced?
stave apply --dry-runAre inputs well-formed before evaluation?

stave apply evaluates safety. stave diff detects change. stave bisect finds the temporal origin of a violation. Use them together: drift finds what changed, bisect finds when the violation started, apply determines if the change made things unsafe.

Key files

FilePurpose
cmd/drift/cmd.goTop-level drift command
internal/core/asset/drift.goComputeDrift, InfrastructureDrift, DriftSummary
internal/core/asset/drift_diff.goDiffAssets, property-level comparison