Use this checklist for a real Atlas deploy to Vercel when the target environment should support:
- Telegram webhook ingestion
- Google Calendar linking and OAuth callback handling
- protected cron routes
This is the deployment runbook for the current locked-down private-beta shape of the app.
- The target branch is reviewed and ready to release.
- The Vercel project is connected to this repo with
apps/webas the Root Directory. - The target database exists and is reachable from the deployment environment.
- The target database has a recent backup or snapshot if the release includes schema changes.
- Google Cloud OAuth credentials exist for the deployed domain.
- You have access to the Telegram bot token and can call the Telegram Bot API.
Set these in the Vercel project for the target environment before deploying:
DATABASE_URLAPP_BASE_URLOPENAI_API_KEYTELEGRAM_BOT_TOKENTELEGRAM_WEBHOOK_SECRETTELEGRAM_ALLOWED_USER_IDSGOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETGOOGLE_OAUTH_REDIRECT_URIGOOGLE_LINK_TOKEN_SECRETGOOGLE_CALENDAR_TOKEN_ENCRYPTION_KEYCRON_SECRET
APP_BASE_URLmust be the canonical public app origin for that environment.GOOGLE_OAUTH_REDIRECT_URImust exactly match the deployed callback URL and the Google Cloud OAuth client configuration.GOOGLE_LINK_TOKEN_SECRET,GOOGLE_CALENDAR_TOKEN_ENCRYPTION_KEY, andCRON_SECRETshould be unique per environment.GOOGLE_CALENDAR_TOKEN_ENCRYPTION_KEYmust be a base64-encoded 32-byte key.TELEGRAM_ALLOWED_USER_IDSis required in all environments. The app should not boot without it.- Vercel Preview and Vercel Production must use different
DATABASE_URLvalues. Preview must not point at the production database.
Set the production database connection string in GitHub Actions secrets as DATABASE_URL_PRODUCTION.
- This secret is for the hosted migration workflow, not for Vercel runtime configuration.
- The migration workflow maps
DATABASE_URL_PRODUCTIONtoDATABASE_URLonly inside the job that runspnpm --filter @atlas/db db:checkandpnpm --filter @atlas/db db:migrate. - Keep the production secret scoped to the repo or environment that owns production releases.
- In GitHub branch protection or rulesets, mark
Apply DB Migrations / apply-db-migrationsas a required status check for the production release path.
- Confirm the release branch is not
mainwhile implementation work is still ongoing. - Run the narrowest relevant verification for the release scope.
- If shared contracts, schemas, or cross-package behavior changed, run
pnpm typecheckandpnpm testif the environment supports it. - Confirm docs are updated for any new env vars, external surfaces, or operator steps.
- Review the pending migration files under
packages/db/drizzle/. - Confirm the migration set matches the code being deployed.
- Confirm the database commands are run only through
packages/db:cd packages/db && DATABASE_URL=... pnpm db:generatecd packages/db && DATABASE_URL=... pnpm db:checkcd packages/db && DATABASE_URL=... pnpm db:migrate- or the equivalent
pnpm --filter @atlas/db ...form withDATABASE_URLalready set
- Merge the release to
mainand let.github/workflows/apply-db-migrations.ymlrun againstDATABASE_URL_PRODUCTION. - Do not apply production migrations from a local shell. The GitHub Actions migration workflow is the only supported production migration runner.
- Confirm the migration workflow succeeds before starting or approving the production Vercel release.
- If the environment has known partial-state risk, verify the release migration is safe to rerun or has an explicit recovery plan.
- Push the release branch and confirm the intended commit SHA.
- Merge the intended commit to
main. - Verify
.github/workflows/apply-db-migrations.ymlpassed on thatmaincommit before promoting any production deploy. - Verify the Vercel project uses
apps/webas the Root Directory and can access workspace files outside that directory. - Confirm the target Vercel environment has all required env vars, including separate Preview and Production
DATABASE_URLvalues. - Deploy the intended
maincommit to Vercel Production. - Confirm the deployment is healthy before switching any production traffic assumptions to it.
If this is a new production domain or the webhook target changed, register the webhook:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-H "content-type: application/json" \
-d '{
"url": "https://YOUR-DEPLOYMENT-DOMAIN/api/telegram/webhook",
"secret_token": "YOUR-TELEGRAM-WEBHOOK-SECRET",
"allowed_updates": ["message", "edited_message"]
}'Verify the webhook status:
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/getWebhookInfo"- Send a plain text message from an allowlisted Telegram account that is not yet linked.
- Confirm the webhook responds successfully.
- Confirm Atlas replies with a Google connect link.
- Confirm no ingress record is created for that unlinked message.
- Open the Telegram-delivered Google connect link.
- Confirm
GET /google-calendar/connectrenders the confirmation page and does not burn the handoff during preview. - Confirm clicking the confirmation action starts Google OAuth.
- Complete OAuth with a real Google account allowed for the configured Google Cloud project.
- Confirm the callback lands on the HTML success page.
- Confirm a linked connection record exists and that stored credentials are encrypted at rest.
- Confirm a follow-up Telegram message from the linked user now goes through normal ingress, planning, and scheduling flow.
- Confirm the GitHub Actions workflows
.github/workflows/send-followups.ymland.github/workflows/reconcile-google-calendar.ymlare enabled withAPP_BASE_URLandCRON_SECRETrepository secrets. - Confirm
send-followups.ymlruns every 15 minutes andreconcile-google-calendar.ymlruns every 30 minutes. - Confirm both workflows expose manual
workflow_dispatchfor smoke tests. - Call the follow-up cron route without
Authorization: Bearer $CRON_SECRETand confirm it is rejected. - Call the follow-up cron route with the correct bearer token and confirm it succeeds.
- Call the reconcile route without
Authorization: Bearer $CRON_SECRETand confirm it is rejected. - Call the reconcile route with the correct bearer token and confirm it succeeds.
- Confirm removed public planner/debug routes are still unavailable.
- Confirm linked-account reads do not expose raw Google access or refresh tokens.
- Confirm Vercel logs do not expose webhook secrets, cron secrets, link tokens, or decrypted Google credentials.
- Confirm the production deploy commit has a successful
apply-db-migrationsGitHub Actions run attached to it.
- If the app deploy is bad but the schema is still compatible, roll traffic back to the last known good deployment.
- If a migration is not backward-compatible, do not assume app rollback alone is sufficient.
- If the migration workflow fails, do not continue with production deploy until the workflow is fixed or the migration issue is explicitly recovered.
- If Google OAuth is failing after deploy, first verify
APP_BASE_URL,GOOGLE_OAUTH_REDIRECT_URI, and the Google Cloud OAuth client configuration all match exactly. - If Telegram delivery fails after deploy, verify webhook registration, secret-token alignment, and Vercel route health before changing app code.
README.mddocs/workflows/vercel-telegram-webhook.mddocs/workflows/feature-delivery.mddocs/decisions/0007-google-calendar-authority-and-sync.mddocs/decisions/0008-security-lockdown-and-google-oauth-handoff.md