A configuration write in OpenWAF has three inputs besides its data: a reason, an optional simulation flag and the expected version of the zone. Every interface uses them the same way.
The reason
Every configuration write needs a reason of 1 to 500 characters (after trimming spaces), without line breaks or other control characters. It is stored in the version and in the audit log. Without it the API answers 422 reason_required.
The only exception is adding a zone: the API accepts it without a reason and records alta de la zona. The CLI still asks for --reason.
The simulation
A simulation runs the whole write, including validation, the new snapshot and the configuration of every edge node that serves the zone, inside a transaction, and then rolls it back. It fails exactly where the real write would fail, and it touches neither the DNS nor the edge. The answer is the plan:
{
"dry_run": true,
"from_version": 7,
"to_version": 8,
"diff": {
"from": 7,
"to": 8,
"changes": [
{"path": "settings.hsts.max_age", "op": "replace", "from": 0, "to": 300}
]
},
"warnings": []
}Each change has a path, an op (add, remove or replace) and the values before and after. Lists are addressed by identity, not position: rules[id=12], records[id=…], certificates[hostname=…]. warnings lists anything the write would leave out, such as a site that could not be built. If to_version equals from_version and the diff is empty, the write would change nothing.
The expected version
The confirmation carries the version you simulated against. If the zone is still at that version, the write is applied and creates the next one. If someone changed the zone in between, nothing is applied and the API answers:
{"error": {"code": "version_conflict", "message": "the configuration changed: the current version is 9"}, "current_version": 9}Read what changed (zones diff, Changes → History), simulate again and review the new plan before confirming. A version conflict never applies part of a change.
Per interface
Every editor opens a dialog with a Preview of the change and a Reason for the change field; Apply change sends it with the version of the preview. On a conflict the dashboard reloads the zone, simulates your change again and asks you to review the new preview.
Every command that changes something requires --reason. The CLI simulates first with the zone's current version, prints the diff and asks ¿Aplicar? [s/N]; it then applies with the same expected version. --dry-run stops after the simulation, --yes skips the question (required when there is no terminal, for example in CI). A conflict ends with exit code 3.
openwaf-cloud ctl zones set example.com hsts.max_age=300 --reason "Enable HSTS" --dry-run
openwaf-cloud ctl zones set example.com hsts.max_age=300 --reason "Enable HSTS" --yes --wait| Header or parameter | Rule |
|---|---|
X-Change-Reason | Required, 1–500 characters. |
If-Match: "<version>" | The expected version. Optional on most writes, required on a revert (428 if_match_required without it). |
?dry_run=1 | Simulate: answer the plan with 200 and change nothing. |
A successful write answers the new version in X-Config-Version and ETag. Reads of a zone and of its edge settings answer the current version in ETag.
curl -sS -X PATCH "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/edge?dry_run=1" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Enable HSTS" \
-H 'If-Match: "7"' \
-H "Content-Type: application/json" \
-d '{"hsts": {"max_age": 300}}'Repeat the request without ?dry_run=1 and with the same If-Match to apply it.
Every write tool takes reason, confirm and expected_version. With confirm false (the default) it only simulates and returns the plan and current_version; your assistant must show you the diff. With confirm: true it requires expected_version and sends it as If-Match.
Changes that are not versioned
Some writes are audited but do not create a zone version:
- Simulated, not versioned: cache purges and origin certificates (issue and revoke). They take a reason and a dry run, but no expected version.
- Not simulated: clients, brand and white-label settings. The CLI's
--dry-runonly shows what it would send and sends nothing. - No reason: checking a zone's delegation now (
zones check) and scanning agent readiness change no configuration.