Files
hope-events/SETTINGS.md
T
joshuaandClaude Opus 5.5 d265047c84 POPIA toolkit part 2: privacy notice editor, operators, consent and access logs
- Admin -> Privacy (POPIA): edit the privacy notice (built-in text as the
  start, placeholders for org/IO/operators), drafts, preview, numbered
  versions; earlier versions readable on /legal/privacy?version=N
- "Significant change" versions are accepted once after login
  (NoticePrompt); sign-up records the version accepted
- Operator register (seeded with the services the app uses), listed in
  the notice via {operators}
- Consent log (news opt-ins, notice acceptances, parental consent) and
  access log (report exports, registers, certificates, group lists, form
  answers, consent/medical lists, backups, impersonation)
- Information Officer on the contact page
- Unit tests (privacyNotice.test.js), e2e (privacy-notice.spec.ts); docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 19:11:20 +02:00

10 KiB

Site Settings — Defaults Reference

Every setting below is stored as a row in the AppSetting key-value table and is editable at runtime via Admin → Site Settings (or, for the first few, the /setup wizard on a fresh install). None of them are required — this page documents exactly what happens when a given key has never been saved.

Settings changes take effect within 60 seconds everywhere (the backend's in-process settings cache TTL) without a server restart.

Organisation

Setting Default when unset Notes
org_name "Cross Code" Falls back to ORG_NAME env var first, then this hardcoded default (the platform vendor's name, used as a neutral placeholder — org_name is a required field in the setup wizard, so this fallback is practically unreachable once a site has been set up). Shown in the navbar, email headers/subjects, and WhatsApp messages.
org_tagline "Connecting community through events" Falls back to ORG_TAGLINE env var first. Shown under the org name in the email header.
org_email (blank) No fallback in most places. Exception: the Terms of Use / Privacy Policy pages default to privacy@example.com (an obvious placeholder) if unset — see the Legal section below.
org_phone / org_address (blank) No fallback — simply omitted wherever shown if unset.
app_base_url APP_BASE_URL or FRONTEND_URL env var, else http://localhost:3001 Used to build links in emails (password reset, ticket delivery, etc.). Should always be set in production.

Branding

Setting Default when unset Notes
primary_color Legacy accent_color value if present, else #4F46E5 (indigo) on the website / #1e3a5f (navy) in emails See Two different fallbacks below — this is the one setting with a genuine inconsistency worth knowing about. Drives buttons, links, nav highlights, and the email header/CTA.
secondary_color 210 40% 96.1% background / near-black text (light gray "wash", shadcn's stock theme) The Branding tab's color picker pre-fills #8B5CF6 as a suggested starting point, but that's only a UI default — if you never hit Save, the actual site uses the light-gray fallback, not violet.
accent_color 210 40% 96.1% background / near-black text (same light gray as Secondary) Same story as Secondary — the picker pre-fills #EC4899 (pink) as a suggestion only.
logo_url Bundled logo (frontend/src/app/church_logo.jpg) Shown in the navbar. This is still the original Hope Family Church artwork checked into the repo — swap the file (or upload a real logo via the Branding tab) before deploying for a different organisation.
favicon_url frontend/public/favicon.ico (the site's original generic favicon) Shown in the browser tab.

Two different fallbacks for primary_color

If primary_color has never been saved (no admin has ever used the new 3-color Branding tab) and there's no legacy accent_color value either, the website and outgoing emails currently disagree on what "no color" means:

  • Website (globals.css / tailwind.config.js static defaults): #4F46E5, a bright indigo.
  • Emails (backend/src/utils/email.js getOrg()): #1e3a5f, a dark navy — unless overridden by the EMAIL_HEADER_COLOR env var.

In practice this only matters on a brand-new install before the setup wizard or Branding tab has been saved even once — the wizard always writes primary_color, so this gap closes itself the moment setup completes.

Notifications

Setting Default when unset Notes
reg_notification_emails REGISTRATIONS_EMAIL env var, else (blank) If truly blank, no internal notification email is sent for new registrations/payments/donations — it's silently skipped, not an error.

Email delivery (SMTP)

Setting Default when unset Notes
smtp_host SMTP_HOST/EMAIL_HOST env var, else (blank) If blank, the app does not send real email — it uses nodemailer's jsonTransport, which just logs the would-be email to the console. This is the single biggest "nothing is configured" gotcha — ticket/payment/password-reset emails silently don't go anywhere until SMTP is set up.
smtp_port 587
smtp_secure false
smtp_user / smtp_pass (blank) Stored AES-256-GCM encrypted (key derived from JWT_SECRET).
smtp_from (blank) If blank, outgoing mail's From: address falls back to the literal no-reply@crosscode.local.

Payments

Managed in the Payments tab (through /api/payments/providers/settings and /api/yoco, not the generic settings endpoints).

Setting Default when unset Notes
payfast_merchant_id / payfast_merchant_key PAYFAST_MERCHANT_ID / PAYFAST_MERCHANT_KEY env vars, else (blank) From the PayFast (or Sandbox) dashboard. Both are needed before PayFast can take payments. The key isn't secret (it's in the payment form), so it's shown in full. Managed through /api/payfast.
payfast_passphrase PAYFAST_PASSPHRASE env var, else (blank) Stored encrypted. Must match the dashboard's salt passphrase exactly, or PayFast refuses payments ("signature does not match").
payfast_mode PAYFAST_MODE env var, else sandbox sandbox or live: where payers are sent and where notifications are confirmed.
payment_providers Yoco enabled JSON { order: [...], enabled: { yoco: true } }: which online payment providers are on, and their order (the first one enabled and set up is the default). Providers not listed yet come last, switched off (Yoco on, as it was the only provider). If none is on and set up, online payments are off.
yoco_secret_key YOCO_SECRET_KEY env var, else (blank) Stored AES-256-GCM encrypted. If blank, online checkouts and sponsorships fail with "online payments are not configured".
yoco_webhook_secret YOCO_WEBHOOK_SECRET env var, else (blank) Stored encrypted. If blank, Yoco webhooks are rejected, so payments aren't confirmed automatically.
yoco_webhook_id (blank) The Yoco webhook whose secret is stored, set when it's generated from the tab. Used to mark it "Active" and to clear the secret when it's deleted.
Setting Default when unset Notes
legal_operator_name "[operator details not yet configured]" Deliberately a generic, obviously-incomplete placeholder rather than any real person's or organisation's name. Unlike org_name, these legal fields are optional and can realistically stay unset on a live site — defaulting them to Cross Code's (or anyone else's) real identity would misrepresent who's legally responsible for that deployment.
legal_io_name "[not yet configured]" Same reasoning as legal_operator_name.
legal_io_email "io@example.com" Rendered as a mailto: link, so the placeholder stays a syntactically valid address on the IANA-reserved example.com domain rather than a bracketed non-email string that would break the link.
legal_website_url "example.com" Same reasoning — used to build an https:// link.
legal_effective_date "[not yet set]" Plain text only, no link — a bracketed placeholder is fine here. Only used by the built-in privacy notice: once a version is published in Admin → Privacy, its publish date is the effective date.
notify_user_on_impersonation "false" When "true", a user is emailed (with the reason) whenever an admin views their account with Impersonate. Set under Site Settings → Legal → Support access.
consent_medical_retention_days 30 Youth events: medical and emergency details from parental consent forms are deleted this many days after the event ends (daily 09:00 job). The consent record itself — who agreed, when, to which text — is kept. Set under Site Settings → Legal.
org_email (as used on legal pages specifically) "privacy@example.com" Different fallback than org_email's general default (blank) — also rendered as a mailto: link, so it needs to stay a valid-looking address.

WhatsApp

Setting Default when unset Notes
WAWP_ACCESS_TOKEN / WAWP_INSTANCE_ID WAWP_ACCESS_TOKEN/WAWP_INSTANCE_ID env vars, else (blank) No hardcoded default — WhatsApp sending is simply unavailable until both are configured (Admin → Site Settings → WhatsApp).
WAWP_WEBHOOK_SECRET Generated (random) the first time the WhatsApp tab is opened Part of the webhook URL shown in Site Settings → WhatsApp (/api/whatsapp/webhook?secret=…). WAWP doesn't sign webhooks, so incoming messages (STOP replies) are only acted on when the secret matches. Not editable in the UI; delete the row to get a new one (then update the URL in WAWP). The URL uses BACKEND_URL.

Privacy (POPIA)

Setting Default when unset Notes
legal_io_email (Information Officer, Site Settings → Legal) — Also where new privacy requests and the daily "requests due" reminder are emailed, together with all admins. The io@example.com placeholder is skipped.
PRIVACY_JOBS_ENABLED (env) true The daily 08:00 privacy job that reminds admins of requests due within a week or overdue.
privacy_notice_draft — The unpublished draft from Admin → Privacy → Privacy notice (JSON). Published versions are in the PrivacyNotice table. With no published version the built-in notice (backend/src/utils/privacyNotice.js) is shown. The notice's org name, Information Officer, website, emails and medical retention days come from the settings above when it's shown.

People's News & announcements opt-in is per account (User.newsEmail, User.newsWhatsApp), not a site setting. Unsubscribe links are signed with JWT_SECRET and don't expire — changing JWT_SECRET breaks links in emails already sent.

Backups

Setting Default when unset Notes
backup_retain_count 14 How many nightly backups to keep on disk (oldest deleted beyond this count). Editable at Admin → Site Settings → Backups. See BACKUP_ENABLED (env var) to turn the nightly job off entirely.

Setup state

Setting Default when unset Notes
setup_complete false (treated as "not true") Controls whether /setup redirects to /login or runs the first-time wizard. Set to "true" automatically when the wizard finishes.