Drift Scanner

Scanning & drift detection

How snapshots, diffs, and severity work. Understand the difference between INFO, WARNING, and BREAKING changes, and how to accept drift as a new baseline.

Drift Scanner works off two primitives: snapshots and diffs. Everything downstream — alerts, the event history, the dashboard summaries — is derived from these two things.

Baselines

The first snapshot captured for an environment becomes its baseline. The baseline is the "what the schema should look like" reference. Drift Scanner compares every subsequent snapshot against it.

You can re-baseline at any time. Accepting drift (see below) tells Drift Scanner "yes, the current state is the new truth" and promotes the latest snapshot to the baseline.

Scheduled scans

On every tick of the environment's cron schedule, Drift Scanner:

  1. Connects to the database using the credentials you registered
  2. Pulls metadata from pg_catalog
  3. Builds a fresh snapshot and stores it
  4. Diffs the new snapshot against the baseline
  5. If the diff is non-empty, creates a drift event and fires alerts

A single scan on a modest database takes a few seconds. The service runs up to 100 concurrent environment scans platform-wide, with a 5-second connection timeout and a bulkhead of 20 concurrent drift scans.

Manual scans

You can trigger an ad-hoc scan from the dashboard or the API (POST /api/v1/drift/environments/{id}/scan). Manual scans are rate-limited per tier:

TierManual scans / minute
Free3
Pro10
Growth30
Scale60

Diff severity

Every change in a diff is assigned a severity. The event's overall severity is the highest severity of any single change.

INFO — safe additions

Changes that cannot break existing workloads. Examples:

  • Adding a new index (non-unique)
  • Adding a new nullable column with a default
  • Adding a new table
  • Adding a new function

WARNING — risky additions

Changes that are additive but can cause runtime pain in the wrong shape. Examples:

  • Adding a NOT NULL column without a default (blocks inserts from older code paths)
  • Adding a unique index on a column that may already contain duplicates
  • Widening a primary key

BREAKING — destructive changes

Changes that remove or rewrite committed schema. Examples:

  • Dropping a column, table, index, or constraint
  • Changing a column type
  • Tightening a nullability constraint (NULLNOT NULL)
  • Removing a foreign key

Each diff item carries severity, table, column, changeType, description, recommendation, and estimatedImpact — so you can surface actionable context in your alerts.

Drift events

When a scan produces a non-empty diff, Drift Scanner writes a DriftEvent with:

  • id — event UUID
  • envId — environment UUID
  • baselineId / currentId — the two snapshot IDs being compared
  • severityINFO, WARNING, or BREAKING
  • breakingCount, warningCount, infoCount
  • acknowledged — did someone mark this as reviewed?
  • detectedAt — when the scan that produced the event ran
  • items — the full list of DiffItems

Events are paginated through GET /api/v1/drift/events.

Acknowledging an event

Call POST /api/v1/drift/events/{id}/acknowledge (or click Acknowledge in the dashboard) to mark an event as reviewed. This does not change the baseline — it just signals that a human has seen it.

Accepting drift (re-baselining)

Acknowledgement and re-baselining are different. Re-baselining promotes the latest snapshot to become the new baseline, so future diffs are measured against it. Use this when:

  • You shipped an intentional migration and want to stop getting alerts about it
  • You manually hot-fixed the schema in production and have since committed it upstream

Re-baselining is available in the dashboard. It is not yet exposed as its own endpoint — use acknowledge + the next scheduled scan for now if you only care about silencing future alerts on already-seen drift.

Comparing two environments

You can also diff two environments directly (e.g. staging vs. production) via POST /api/v1/drift/compare — useful for "did my staging soak miss something?" audits. See the API reference for the request shape.