Evergrn โ€” Go-Live Checklist

Last updated: June 28, 2026

Single source of truth for launch readiness. All other documents defer here โ€” do not track go-live status in roadmap.md, cloud-infrastructure.md, or vulnerabilities.md.


โœ… Complete

Item Notes
Azure Blob Storage All uploads stream to evergrnuploads via src/config/blob.js โ€” no local disk
Application Insights Conditional SDK init in server.js; auto-instruments requests, exceptions, dependencies
CORS allowlist Custom middleware in src/app.js; locked to evergrn.co, staging.evergrn.co, localhost:5173
Input validation (Zod) All 18 body-receiving routes validated via src/middleware/validate.js + src/schemas/index.js
GodMode gated (dev-only, permanently) API: /admin routes conditionally required only when NODE_ENV=development; Web: /godmode route wrapped in import.meta.env.DEV โ€” Vite tree-shakes it from all builds. Intentionally kept in dev codebase.
Payment hold flow capture_method: 'manual' on quote accept; HELD + releaseAt set on job COMPLETED in src/routes/jobs.js
ProviderPayout schema Model fully wired in prisma/schema.prisma; migration applied to dev and staging
Payout batch cron src/jobs/payoutBatch.js โ€” capture โ†’ Stripe transfer โ†’ ledger; runs via node-cron at 8 AM ET in server.js. Idempotency guarded.
Stripe Connect onboarding /payouts/status, /payouts/balance, PayoutComplete, PayoutRefresh pages all live
Security headers helmet in src/app.js: CSP, HSTS (1yr), X-Frame-Options: DENY, removes X-Powered-By
Seed script guards NODE_ENV=production guard prepended to all 21 scripts/seed*.js โ€” won't run against prod DB
Staff portal Replaces GodMode for production use โ€” 4 roles, elevation flow, 15-min tokens, full audit log
Provider ID document OCR POST /providers/me/id-documents โ†’ Azure Document Intelligence prebuilt-idDocument; validates confidence, docType, DOB presence; stores idCheck* on Provider
Provider insurance document OCR POST /providers/me/insurance-document โ†’ prebuilt-read; extracts carrier/policy/dates/coverage; stores insCheck*; insuranceInfo image-only โ€” no manual entry from mobile
In-app support tickets POST /users/me/support + POST /providers/me/support โ†’ email to kdavis@evergrn.co; categories: technical
Message photo upload POST /jobs/:id/messages/photo โ†’ messages blob container; SAS URL embedded in thread
APNs push notifications Key SSW3R447H4 (Team 2QH59J6E6Y) uploaded to EAS. End-to-end delivery verified โ€” new message and quote-received pushes confirmed on real device

โŒ Remaining Blockers

Item Effort Notes
Production Azure infra + App Store build 1โ€“2 days Provision evergrn-api-prod App Service B1, evergrn-db-prod PostgreSQL B1ms, point evergrn.co / api.evergrn.co DNS, apply all migrations, set App Settings. Then run eas build --profile production โ€” eas.json already sets EXPO_PUBLIC_API_URL=https://api.evergrn.co for that profile. Mobile BASE_URL resolves automatically, no code change needed.

๐Ÿ”ต Intentionally Deferred (Post-Launch)

These are not blockers for MVP launch on a single App Service instance. They become required before scaling or for full production hardening.

Item Why Deferred When Required
Redis notification queue In-memory Map in src/config/notificationQueue.js works on a single instance Before adding a second App Service instance
Redis rate limiting store In-memory limiter is accurate on a single instance; swap to rate-limit-redis at scale Before adding a second App Service instance
Redis token revocation blocklist 7-day JWT lifetime is acceptable at MVP scale on a single node; full production hardens this Before full production launch / horizontal scaling
Payout batch โ†’ Azure Functions Timer Trigger node-cron runs correctly on a single instance; risk is a crash at 8 AM delays payouts by one day Before running more than one instance (duplicate cron runs would race, though idempotency guard prevents double-pay)
Dockerfile + Container Apps migration Not needed for App Service deploy; required for horizontal scaling Phase 2 per cloud-infrastructure.md

๐Ÿ”’ Security Items (Tracked Here, Not in vulnerabilities.md)

Item Status
bcrypt password hashing (12 rounds) โœ…
JWT signature verification on every request โœ…
Stripe webhook signature verification โœ…
Stripe capture_method: 'manual' for payment holds โœ…
Stripe payment auth blocks quote acceptance โœ… Authorization runs before DB transaction โ€” declined card returns 402, job never reaches ACCEPTED. PaymentIntent cancelled if transaction subsequently fails.
Azure Blob Storage for all file uploads โœ…
Provider-ZIP + service validation on quote submit โœ… Server-side check in POST /quotes โ€” 403 if job ZIP or service type not in provider's list
CORS allowlist โœ…
Zod input validation (all body-receiving routes) โœ…
Security headers via helmet (CSP, HSTS, X-Frame-Options) โœ…
GodMode excluded from staging/production โœ…
Seed script production guards โœ…
Staff elevation audit log (AuditEvent) โœ…
Provider identity document OCR validation (confidence, docType, DOB checks) โœ…
Provider insurance certificate OCR (carrier, policy, dates, coverage extraction) โœ…
Expo SecureStore for mobile token storage โœ…
Admin login rate limiting (10/15 min) โœ…
Auth endpoint rate limiting (/auth/login, /auth/provider/login) โœ… 10 attempts/15 min โ€” loginLimiter in src/routes/auth.js
httpOnly cookie token storage (web) ๐Ÿ”ต Post-launch
Token revocation / blocklist ๐Ÿ”ต Production (full launch) โ€” Redis blocklist in authenticate middleware. MVP on single instance is acceptable with 7-day JWT lifetime.
MIME content verification for uploads (file-type package) ๐Ÿ”ต Post-launch