Configuration / Operations Settings
Runtime Overrides
The names on KV_OVERRIDABLE can be overridden from the CONFIG_KV namespace under the key
settings, as one JSON object. One key covers them all: a single read is atomic, costs one of the
100,000 daily KV reads instead of one per lever, and gives an operator one place to see every
override in force.
{ "RENDER_BUDGET_MS": 4000, "FILL_BATCH_SIZE": 8, "PREFILL": "0" }Reading and Writing Overrides
GET /settings reports every lever with its value and its source (kv, var or default);
PUT /settings merges a JSON patch. Both take the owner token.
curl -H "authorization: Bearer $OWNER_TOKEN" https://your-site/settings
curl -X PUT -H "authorization: Bearer $OWNER_TOKEN" -H 'content-type: application/json' \
-d '{"RENDER_BUDGET_MS": 4000, "PREFILL": "0"}' https://your-site/settingsAn unlisted name is refused and named in the reply rather than dropped in silence. The allow-list is enforced at the writer as well as the reader: a reader-side filter makes an unlisted name inert, and a writer-side filter makes it unstorable.
The route is answered by the front Worker with no object hop: CONFIG_KV is a front-Worker binding,
so reaching it through the Durable Object would spend a request to get at a namespace the isolate
already holds.
From Drupal, Without the Owner Token
The same levers are editable at /admin/config/drupflare/settings, gated on the
administer drupflare site permission. Each field holds the stored override, with the deployed value
beside it and its source named, so an operator can tell an override they chose from a default nobody
has looked at. Only the fields that changed are sent, because a patch carrying every field would turn
a deployed value into a stored override by the act of pressing save.
Every lever has a domain, LEVER_DOMAINS in src/ops/plan.ts: a flag is 0 or 1, a number has
the range its reader clamps to, and a fixed choice (OPCACHE_MODE, MAIL_TRANSPORT,
SITE_LOCATION_HINT) is one of its listed values. The form offers a select or a bounded number
field from it, and writeSettings() refuses a value outside it and names the reason, so a mistyped
value cannot reach a reader. LAZY_FS_BUDGET_BYTES accepts values up to 8 MiB.
The Three Permissions
| permission | reaches |
|---|---|
view drupflare status |
the runtime status page, read only |
administer drupflare site |
the levers, and the operations terminal's cache and queue work |
administer drupflare owner |
code delivery, including the terminal's en and package lines |
administer drupflare owner is granted at claim, through a Site Owner role given to the claimed
account, and only an account that already holds it can grant it. Holding administer permissions is
not enough: the permission forms disable its checkboxes, and a role or user save that adds or removes
it is put back when the acting user lacks it. An administrator role carries every permission, so
assigning one is the same grant. The owner token outranks all three and works when Drupal does not.
A site created before the tiers reconciles onto them: the five older names map to the three, and uid 1 is given the owner role.
PLAN is not on that form and cannot be written through it. Every lever there has a worst case
of a slower site; PLAN selects a limits profile whose quotas are account-wide, while whoever
reaches a Drupal form administers one tenant. It stays on the owner-token route above, and the host
capability behind the form refuses it at every spelling.
/admin/modules/drupflare is the matching read-only page for code delivery: it names the three
delivery paths and lists what has been delivered to this site. Delivering code stays an owner action
on /_cfw/git, for the same tenancy reason. Within the operations terminal, en and any package
line additionally require administer drupflare owner, so a site can grant the terminal without
granting the ability to add code to the runtime.
The front Worker and the Durable Object each hold their own copy of the bindings, so the overrides are
applied inside the object too. adoptSettings() overlays every name on the allow-list in the object,
and it runs from alarm() as well as handle(), because several levers are read on the fill chain and
an alarm never passes through handle(). The fast storage lane adopts nothing: it is await-free by
construction and reads no lever.
A lever is consulted from KV before its var. An operator can change a setting without a redeploy, and the var is the fallback.
The list is an allow-list and it is a privilege boundary. KV is operator-writable, so merging an
arbitrary object into the environment would let anyone with KV write set PW_DIAGNOSTICS=1. Every
name here is a performance lever whose worst case is a slow site; none changes what is reachable.
PLAN has its own key, resolver and authorisation for the reason given above. The mail credentials
are absent for the same reason: MAIL_TRANSPORT only chooses between transports the deploy already
configured, while a KV writer who could set SMTP_HOST would receive every password-reset link the
site sends.
A malformed document, an unknown key and a KV error all yield no overrides, and none of them throws. Values are read once per isolate per minute.