Runtime configuration lives in the database, not in environment variables. Env holds
credentials; the Setting table holds everything an admin can change.
The pieces
Setting is a key-value table with JSON values.
src/content/contentSchema.ts types and validates that content.
getContent() in src/lib/settings.ts reads it, applying defaults for anything unset.
getStrings() layers StringOverride rows on top of the compiled STRINGS, so an admin
can change an individual string without a deployment. See
the strings rule.
Event state derivation
src/lib/event-state.ts exports deriveEventState(), which turns the stored mode and
switches into the booleans the app actually branches on: whether registration is accepted,
whether payments are required, which public sections render.
Read the derived state, never the raw setting. Payments come from the event's own
paymentsEnabled switch, not from its kind: an Intra MUN is free because its switch is off,
and that is what makes allotment confirm a delegate immediately instead of raising a payment.
The admin workspace asks getEventCapabilities() in src/lib/event.ts instead
(intra, payments, crossDelegations, matrixPublic). The UI hides what is off, and
server actions call requireCapability() so a hidden tab is never the only guard. Cross
delegations are never offered for an Intra MUN; for a conference they follow the Setup
switch, and stay visible while any already exist.
The eight section switches
content.publicSections.* gates the homepage blocks, the header and the footer from the same
value, so a hidden section can never be linked to from navigation.
Where settings are edited
| Page | Holds |
|---|---|
/admin/config | The event: kind, publish, registration, matrix, payments, details |
/admin/site | Society copy, awards, contacts, and the society section switches |
/admin/config/committees | Committees, matrix, matrix visibility |
/admin/config/money | Fees, and admin-only payment routing |
Adding a setting
-
Add it to
contentSchema.tswith a default. Every setting needs one; a missing key must not be a crash. -
Read it through
getContent(). -
If it changes behaviour rather than copy, derive it in
event-state.tsand read the derived value. -
Add the control to the right config page.
-
Run
npm run check.check-settings.tsandcheck-event-state.tscover this.
Why not environment variables
Because the people who change these are not the people who deploy. A conference's dates, venue, fees and mode are edited by the secretariat during the run-up, sometimes several times a day. Requiring a deployment for each would put an engineer in the loop for copy changes.