Product
The product
Three parts, each with one job: the collector writes drift snapshots, the database keeps them behind two narrow roles, and the viewer reads them.
The collector
apps/collector runs env-sync diff --format json as a subprocess, once per selector in the filtered inventory, and writes one collector run plus its drift snapshots in one transaction.
Before it collects, pre-flight checks every service against a reviewed allowlist and a probe proves that the pinned env-sync refuses targets outside its scope.
The database
packages/db is a Drizzle schema plus hand-written SQL: three tables (collector_runs, drift_snapshots, drift_snapshot_keys), a collector role that can only insert, a viewer role that can only select, and forced row-level security.
Each key row holds the key name, its key status and two 12-character fingerprints. There is no column for a secret value.
The viewer
apps/web is a read-only Next.js dashboard. It builds a minimal fleet view on the server, with no fingerprints in it, and renders the targets and the last 20 collector runs.
An access gate checks every request: a Supabase session and a confirmed email on the viewer allowlist.
At a glance
- Runs on
- Your infrastructure; there is no hosted service
- Collector
- A scheduled job (every 6 hours in the shipped workflow)
- Storage
- Postgres, in an env_sync schema
- Shows
- Key names and key states; never values or fingerprints
- Writes to targets
- Never: it only runs env-sync diff
- Source code
- Private repository; access by request
Know which secrets drifted, without seeing them
env-sync-viewer is self-hosted: you run the collector and the viewer in your own infrastructure. Access to the repository is by request.