Platform settings
Everything the platform team configures without a deploy, at /admin/settings. One section in the sidebar, several pages behind a sub-navigation rail (docs/ui-ux-spec.md §7.6). Last updated 2026-09-04.
1. Pages
| Page | What it holds |
|---|---|
| General | Public name, support email, and the AI provider every copilot and AI feature routes through |
| Service plans | The commercial plans offered to schools and to vendors |
| Modules | Optional platform capabilities, each a toggle row with its own settings |
Every email type: on/off, and the template with its version history (docs/email.md) | |
| Trial credits | Guardrails on pilot economics, with live spend against the cap |
| Platform users | EduSpaze admin accounts |
| Billing | How payments work in production, and the annualised value of the configured plans |
2. Where values live
Simple values live in the Setting key/value table and are described once in lib/settings/registry.ts: label, type, default, bounds and help text. The settings pages render from that registry, so adding a setting is a line there plus wherever it is read. lib/settings/index.ts provides readSettings, writeSettings, isModuleEnabled and numberSetting; a key that was never written falls back to its registry default.
Service plans are a table (ServicePlan), because a plan has several fields and a list of benefits.
3. Modules
A module is a capability that can be switched off. Switching it off hides it and stops its behaviour; nothing it already recorded is deleted, and its settings keep their values.
| Module | Key | What the switch does | Settings while on |
|---|---|---|---|
| Parent access | parent_access_module | Makes the guardian portal available. Each school still has its own switch, so this only makes it possible | — |
| Engagement tracking | engagement_tracking | Off refuses heartbeats (/api/telemetry/heartbeat returns 503) and stops new sessions being measured | Inactivity gap, launch-only minutes, heartbeat interval — the platform defaults a school inherits when it has not set its own |
| EduSpaze support sessions | support_sessions | Platform kill switch for support sessions: off, no session can start even where a school has allowed access | Maximum session length |
| Public registration | public_registration | Off closes /register/* and turns every "register" call to action on the public site into Contact us for more info, pointing at /contact | Schools may register · Vendors may apply, independently |
| Challenge publishing | challenge_publishing | Off closes the challenge channel | The mode: approve before publishing · publish immediately · let AI screen (docs/email.md §6) |
| Escalation alerts | escalation_alerts | Off stops the ageing checks emailing the platform team | A threshold per check (docs/email.md §7) |
4. Service plans
A plan is the commercial description of a tier the platform already enforces (SchoolTier for schools, StartupTier for vendors), so editing a plan changes what a tier is called, what it costs and what it promises, and never introduces a second source of truth about entitlement. One plan per audience is the default assigned on signup. A plan with customers on its tier cannot be deleted.
5. Audit
Every settings write, plan change, module flip and platform-user action goes through the audited client, so it lands on the platform audit chain with the admin as actor and the before and after values (docs/audit-trail.md). Setting and ServicePlan are PLATFORM-scoped, so they appear in Admin → Audit, not in a school's log.
6. Guardrails on platform users
Everyone under Platform users has full access to every school and vendor. Two rules stop the platform locking itself out or being quietly taken over:
- You cannot disable or reset your own login there — use Account & settings.
- The last active admin cannot be disabled.
7. Adding a setting
- Add the field to
lib/settings/registry.ts(a module'sfields,GENERAL_FIELDSorCREDIT_FIELDS) with a label, type, default and bounds. - Read it with
readSettings/numberSetting/isModuleEnabledwhere it applies. - Seed a value in
prisma/seed.tsif the demo should show something other than the default.
No page changes are needed: the settings pages render from the registry.
