Tunable values are runtime settings: stored in the service's own database and edited by super admins in the Tawa Console. A redeploy or a code change is never needed to flip a value.
off / shadow / enforce)Use tawa config (see config) for values that wiring needs before the database is reachable
or that must never be visible in a UI:
sub values allowed to write settings)If changing the value is a product or operations decision, it is a setting. If changing it reconnects the service to something, it is config.
Reference implementation: iec-janus, src/services/settings.ts and src/routes/adminSettings.ts.
authz, gas.buyUrl,
promo.launchGrantsEnabled), each with a zod schema, a defaultValue, a one-sentence
description, and an editor hint: 'boolean' | 'number' | 'string' | 'url' | 'json'.before, after, by and at.getSetting(key) is synchronous, never throws, and returns the registry default when no value
is stored or the stored value fails its schema.version. A write may send expectedVersion;
a mismatch is rejected so two admins cannot overwrite each other.getSetting on every run,
never once at boot, so a flag flipped in the Console takes effect on the next run.const SETTINGS = {
'promo.launchGrantsEnabled': {
schema: z.boolean(),
defaultValue: () => false,
description: 'Run the launch promo-grant cycle for eligible agency orgs.',
editor: 'boolean',
},
} as const
if (getSetting('promo.launchGrantsEnabled')) await runLaunchGrantCycle()
Mounted under the service's own prefix (<prefix>).
| Route | Purpose |
|---|---|
GET <prefix>/admin/settings | List every declared setting |
PUT <prefix>/admin/settings/:key | Set one value. Body: { "value": ..., "expectedVersion": 3 } (expectedVersion optional) |
GET <prefix>/admin/settings/:key/history?limit= | Audit rows for one key, newest first |
The list response:
{
"settings": [
{
"key": "gas.buyUrl",
"value": "https://example.com/buy",
"default": "",
"version": 4,
"source": "stored",
"updatedBy": "<bio-id sub>",
"updatedAt": "2026-10-04T12:00:00.000Z",
"description": "Where the 402 INSUFFICIENT_GAS response sends a caller to buy gas. Empty = no link.",
"editor": "url"
}
],
"canWrite": true,
"actor": "<bio-id sub>"
}
source is stored (a value is in the database) or default (the registry default; version 0).
updatedBy and updatedAt are absent for a key that has never been written.
sub in the service's owners list.403. A service, script or agent holding the
internal key cannot change a setting.canWrite and actor in the list response tell the UI whether to render fields as editable.
The Tawa Console (tawa-web) renders every declared setting generically from description and
editor. Adding a key to the registry is all it takes; no Console change or deploy is needed.
| Service | Console page |
|---|---|
| iec-janus | Console → Janus Switches |
| iec-wallet | Console → Wallet Switches |
A PR that adds a tunable env var or config var is changed to a setting before merge. Reviewers ask one question: could someone want to change this without a deploy? If yes, it goes in the registry.
Last updated: October 6, 2026