# Write gates

> Control write categories with settings-based gates. Learn the CLI defaults, explicit web permissions and operator commands.

Reads run freely. Every tool that changes the site is behind a gate, by
category, so you can let an agent create content without also letting it
run raw SQL.

## Where the gates live

The gates are flags in droost's own settings file, `settings.droost.php`,
beside `settings.php`. They are never in exported configuration, so an
armed gate cannot travel to another site with a config import.

On a CLI call, ordinary write categories are enabled unless explicitly disabled,
while PHP evaluation and raw SQL writes stay off until explicitly enabled. An agent on a local site can already run PHP and SQL through drush, so
those two are a switch for you, not a default.

## The gates

| Gate | Unlocks |
|---|---|
| `allow_destructive` | fallback for ordinary write categories with no explicit value; excludes PHP evaluation and raw SQL writes |
| `allow_scaffold` | `droost_structure_create` |
| `allow_entity_write` | entity create, update and delete, `droost_canvas_tree_set`, `droost_generate` |
| `allow_db_write` | `droost_db_create`, `droost_db_update`, `droost_db_delete` |
| `allow_config_write` | `droost_config_set`, `droost_views_compose`, `droost_views_alter`, `droost_canvas_component_toggle`, `droost_canvas_variant_tree_set`, `droost_config_import_one` |
| `allow_module_ops` | `droost_module_install`, `droost_module_uninstall`, `droost_cron_run`, `droost_deploy_hook_reset` |
| `allow_eval` | `droost_eval` (arbitrary PHP) |

An explicit category value takes precedence over `allow_destructive`. Inspect
the resolved gate states rather than assuming the master overrides every flag.

## Arming a gate

```bash
drush droost:gate                              # every gate and its state
drush droost:gate allow_eval on                # turn on PHP evaluation, locally
drush droost:gate allow_destructive off        # disable otherwise-unset ordinary write categories
```

`drush droost:gate <gate> on` requires an interactive terminal. The intended
workflow is for the agent to request a change and for the operator to run the
command. This terminal check does not isolate an agent that already has full
shell access or permission to edit the settings file. A gate is read on every
call, so a change takes effect at once. `/admin/config/development/droost` shows
every gate's state.

## Over the web

Over the web, the development default does not apply: a write runs only
when `settings.droost.php` arms its gate explicitly, and a gate nobody set is
off. The build and developer tools refuse every web request, armed or not. See
[Over MCP, for editors](https://droost.org/docs/module-mcp.md).

## A refusal is the gate working

When a write refuses, the message names the gate. Arm it if you mean it,
or leave it off.

## Safety notes

- **Secret redaction is a best effort.** `droost_config_get` and
  `droost_entity_load` hide values whose key looks secret (`password`,
  `token`, `key`, `credential`, `salt`, `oauth`…). It is a check on key
  names, not on content.
- **SQL is split by intent.** `droost_db_read` runs reads; each write tool
  runs one statement of its own verb, and refuses an `UPDATE` or `DELETE`
  with no `WHERE`. For reads that are read-only in the database itself, add
  a `droost_readonly` connection backed by a `SELECT`-only user:
  `droost_db_read` uses it when it exists.
