Declarative state management for Vercel projects: describe env vars, domains, and crons in a versioned state file, then pull, validate, plan, drift-check, and apply with policy enforcement, restore-point snapshots, and a double-safe write posture.
npm install
npm run build
node dist/index.js --help # or: npm link && vercel-state --helpRequires Node 20+. No runtime dependencies.
export VERCEL_TOKEN=... # recommended, especially in CI
vercel-state auth login # or store a profile (0600, ~/.config/vercel-state/)
vercel-state auth status# snapshot live state as a canonical, commit-ready file
vercel-state pull --project prj_123 > state.json
vercel-state validate -f state.json
# preview changes (read-only); policy violations exit 5
vercel-state plan -f state.json --current-file current.json --policy policy.json
# CI gate: exit 1 when live state departs from desired
vercel-state drift -f state.json --current-file current.json --json
# reconcile — DRY-RUN BY DEFAULT
vercel-state apply -f state.json --project prj_123
vercel-state apply -f state.json --project prj_123 --yes # non-destructive ops
vercel-state apply -f state.json --project prj_123 --yes --force # deletes/env rewrites too{
"schemaVersion": "vercel-state/v1alpha1",
"project": "prj_123",
"env": [
{ "key": "DATABASE_URL", "target": ["production"], "valueFrom": "PROD_DB_URL", "sensitive": true }
],
"crons": [{ "path": "/api/cleanup", "schedule": "0 3 * * *" }],
"domains": [{ "name": "example.com" }]
}Validation is strict: unknown fields fail, and plaintext env values are rejected at the schema level — entries reference valueFrom (a local env var resolved at apply time) instead. pull never fetches values, only metadata.
- Plans are deterministic and idempotent; deletes and env updates are flagged destructive.
applyis dry-run by default;--yesexecutes; destructive ops additionally require--force— and the engine enforces the same gate internally.- A restore-point snapshot of the live state is written before the first write of every apply.
- Policy packs (
require-sensitiveglobs, forbidden keys, cron path prefixes,deny-delete) blockapply/planbut never read-onlydriftruns.
0 ok · 1 drift found (from drift) / runtime error · 2 validation · 3 auth · 4 transport/rate-limit · 5 policy
See SECURITY.md for the audited posture.
# reconcile back to a restore-point bundle written by a previous apply —
# same dry-run default and --yes/--force gates, plus a fresh pre-restore snapshot
vercel-state restore .vercel-state/snapshots/prj_123-....json --project prj_123Crons now participate in pull and drift detection (they're read from the project). Because crons are defined by the production deployment (vercel.json), reconciling them means redeploying — apply reports cron ops as skipped-unmodeled rather than pretending to write them.
- Feature flags are not modeled (beta API surface); the official CLI is the escape hatch.
- Publishing to npm needs the trusted-publisher config on npmjs.com plus a version tag (workflow already shipped).