Files
WebForm-mw/DEPLOYMENT.md

4.8 KiB

Deployment

This app is built to deploy to Render as a Web Service via the included render.yaml blueprint.

Pre-flight checklist

Before deploying, confirm:

  • CiviCRM has a working API key for a service user with sufficient privileges to read/write Contact, Activity, Relationship, and OptionValue.
  • The Primary Contact (or whatever value FORM_CONTACT_RELATIONSHIP resolves to in config/form.ts) relationship type exists, with Individual on side A and Organization on side B.
  • The Check-in (organizing) activity type exists with the six custom field groups attached (Check_in_data__organizing_, Stage_1, Stage_2, Stage_3, Stage_4, Stage_5).
  • The Framework Stage option group on the Organization contact contains all six in-scope values: Inquiry, Organizing, Feasibility, Business feasibility, Store Implementation, Stabilize newly opened co-op.

Verify all of the above against your live CiviCRM by hitting /api/health (in development; production requires a token — see below).

First-time Render setup

  1. Create a new Web Service from this repo on Render. Render will detect render.yaml and configure the service automatically.

  2. Set the secrets in the Render dashboard → Environment. The render.yaml declares them but marks them sync: false so they're never echoed in the committed file:

    Variable Required? Purpose
    CIVI_BASE_URL yes e.g. https://crm.fci.coop
    CIVI_API_KEY yes Per-user API key. Generate via CiviCRM Contact → API Key field.
    CIVI_SITE_KEY yes From civicrm.settings.php (CIVICRM_SITE_KEY).
    CIVI_HTTP_AUTH_USER only if Civi sits behind webserver-level Basic Auth HTTP Basic Auth username.
    CIVI_HTTP_AUTH_PASS only if CIVI_HTTP_AUTH_USER is set HTTP Basic Auth password.
    HEALTH_TOKEN recommended Long random string (e.g. openssl rand -hex 32). Required to access /api/health in production. If unset, that route returns 404.
    PUBLIC_ORIGIN optional e.g. https://check-in.fci.coop — used in absolute self-links if needed later.
  3. Trigger the first deploy. Render will run npm ci && npm run build then npm run start. The platform health check hits /healthz (lightweight, no Civi dependency).

  4. Verify:

    • https://<your-render-url>/healthz{"ok":true,"service":"coop-checkin"}
    • https://<your-render-url>/api/health?token=<HEALTH_TOKEN> → all checks should be green
    • Visit https://<your-render-url>/?cid=<test-individual>&cs=<their-checksum> → form loads with prefill
  5. Custom domain (optional) — Render dashboard → Custom Domain → add check-in.fci.coop. Update DNS to the provided CNAME. Render auto-issues a Let's Encrypt cert.

Security defaults

The app ships with these defaults; review and adjust per your threat model.

Concern Default
HTTPS Required. Render terminates TLS for you. HSTS header is set with a 2-year max-age + preload.
CSP Tight: same-origin scripts/connections; Google Fonts allowed for next/font; frame-ancestors 'none'; no third-party scripts.
Frame embedding Denied (X-Frame-Options + CSP frame-ancestors).
Referrer same-origin so the cid+cs query string never leaks via Referer to other origins.
Health endpoint Production: gated behind HEALTH_TOKEN. Dev: unauthenticated.
Submit rate limit 10 submissions per IP per minute (in-memory; resets on restart).
Error messages Production responses say "Could not save" without details; full error logged server-side with secrets redacted.
Stub mode Refuses to start if NODE_ENV=production and any of CIVI_BASE_URL/CIVI_API_KEY/CIVI_SITE_KEY is missing.

What's intentionally NOT done

  • CSRF tokens. The submit endpoint is gated by the per-contact CiviCRM checksum, which is not browser-cookie-based, so traditional CSRF doesn't apply. Add CSRF tokens if you ever introduce a session-cookie auth path.
  • Multi-instance rate limiting. The in-memory token bucket is per-process. Acceptable for a single Render instance; switch to Redis or Render's edge rate-limit if you scale horizontally.
  • WAF. None configured. Render's platform provides basic DDoS protection; add a WAF (Cloudflare in front of Render, or similar) if your threat model warrants it.
  • i18n. English-only.

Rolling out updates

  1. Push to your default branch.
  2. Render auto-builds and deploys (per autoDeploy: true in render.yaml).
  3. Watch deploy logs for any health-check failures.
  4. After deploy: hit /api/health?token=… to confirm all checks still pass against the live CRM.

Rollback

Render keeps the previous deploy available. From the Render dashboard → Deploys → previous deploy → Rollback to this deploy.