All Docs
FeaturesMaking Tax DigitalUpdated March 7, 2026

Migration Endpoint Security (MIGRATION_SECRET)

Migration Endpoint Security

The /api/migrate endpoint applies pending database schema migrations against the production database. From v1.0.318 onwards, this endpoint is protected exclusively by a dedicated MIGRATION_SECRET environment variable. The previous fallback to NEXTAUTH_SECRET has been removed.

Why a Dedicated Secret?

Previously, /api/migrate fell back to NEXTAUTH_SECRET when MIGRATION_SECRET was not set. This meant:

  • A single leaked credential granted access to both session signing and database schema changes.
  • There was no clear signal to operators that a separate secret was expected.

Separating the secrets limits blast radius: a leaked NEXTAUTH_SECRET can no longer be used to trigger migrations.

Setup

1. Generate a dedicated secret

openssl rand -hex 32

This value must not be the same as your NEXTAUTH_SECRET.

2. Set the environment variable

Add MIGRATION_SECRET to your deployment platform (Vercel, Railway, etc.) and to your local .env file:

# .env
MIGRATION_SECRET=<your-generated-secret>   # Must differ from NEXTAUTH_SECRET

3. Verify at startup

When the server starts, instrumentation.ts validates the secret automatically. In production, the server will refuse to start if either of the following conditions is true:

  • MIGRATION_SECRET is absent or empty.
  • MIGRATION_SECRET equals NEXTAUTH_SECRET.

In development, the same conditions produce a console.warn warning instead of a fatal error, so local environments without all secrets configured are not blocked.

Calling the Endpoint

curl -X POST https://your-app.vercel.app/api/migrate \
     -H "Authorization: Bearer $MIGRATION_SECRET"

The endpoint is also called automatically from instrumentation.ts on every server cold-start, so manual calls are only needed when you want to apply migrations without redeploying.

Response Codes

StatusMeaning
200 OKMigrations applied successfully.
401 UnauthorizedBearer token does not match MIGRATION_SECRET.
503 Service UnavailableMIGRATION_SECRET is not configured on the server.

Boot-time Validation Behaviour

The validateMigrationSecret() function in src/instrumentation.ts runs at module load, before any request is handled.

EnvironmentMIGRATION_SECRET absentMIGRATION_SECRET === NEXTAUTH_SECRET
ProductionFatal — server refuses to startFatal — server refuses to start
Development / Testconsole.warnconsole.warn
Build (NEXT_PHASE=phase-production-build)SkippedSkipped

Migration from Earlier Versions

If you are upgrading from a version prior to v1.0.318:

  1. Generate a new secret with openssl rand -hex 32.
  2. Set it as MIGRATION_SECRET in all production environments before deploying.
  3. Ensure the value differs from NEXTAUTH_SECRET.
  4. Update any CI/CD scripts or runbooks that called /api/migrate with the NEXTAUTH_SECRET bearer token — they must now use MIGRATION_SECRET.

Note: For additional hardening, consider removing the /api/migrate HTTP endpoint entirely and relying on CI/CD pipeline migrations (e.g. drizzle-kit push or drizzle-kit migrate run from a trusted build step). This removes the attack surface entirely.