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 |