Skip to content

edge.* filter fields for the validator's client class (#481) - #69

Merged
cport1 merged 1 commit into
mainfrom
feat/read-edge-class-481
Jul 30, 2026
Merged

edge.* filter fields for the validator's client class (#481)#69
cport1 merged 1 commit into
mainfrom
feat/read-edge-class-481

Conversation

@cport1

@cport1 cport1 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Part of WebDecoy/app#481. Pairs with WebDecoy/app (worker emits x-wd-class) and WebDecoy/node#12 (Node SDK reads it).

This is discoverability, not new capability

Matching the edge tag already worked on a stock install. collect_request_headers() forwards the whole $_SERVER['HTTP_*'] set into the rule context, so this has always been a valid expression:

req.header("x-wd-class") == "script"

Nobody knew, because nothing said so. The header was documented as "watch it in your logs" and the Fields line on the settings screen never mentioned it.

Now: edge.class, edge.clearance, edge.present, plus shorthands edge.verified / edge.crawler / edge.script / edge.browser. One spelling instead of one per site owner, and it survives us renaming a header. The raw-header form still works, with a test pinning that so nobody has to migrate.

Absence is not a class

A rule using this decides whether to serve someone less, so:

  • edge.class is null when the validator did not classify, making a comparison against it false rather than accidentally true
  • a value outside the closed set is dropped — outside it means version skew or something that is not our worker
  • edge.present false means "no information", not "human", and the settings screen says so in those words

Derived from the headers already on RuleContext rather than added as a constructor argument, so a released plugin's RuleContext signature does not change under anyone.

The cache caveat is in the UI, not only the docs

The settings screen now states that these fields are safe for blocking, throttling, logging and metering — and that serving different page content on a cacheable URL is not:

Cloudflare's cache key ignores this header outside Enterprise plans, so the first version cached is served to everyone including Googlebot, and on a cache hit your site never runs at all.

A site owner reading the field list is exactly the person about to make that mistake.

Testing

php tests/run.php — 84 passed, 0 failed, including four new cases: the fields resolving, absence yielding false for every one of them, an unrecognised value being dropped, and case/whitespace tolerance.

Not in this PR

No version bump. Releasing is a separate decision and goes through bin/release-all.sh.

…481)

Matching the edge tag already worked on a stock install: collect_request_headers()
forwards the whole $_SERVER['HTTP_*'] set into the rule context, so
req.header("x-wd-class") == "script" has always been a valid expression. Nobody
knew, because nothing said so — the header was documented as "watch it in your
logs" and the Fields line in the settings screen never mentioned it.

So this is discoverability, not capability: edge.class, edge.clearance,
edge.present, plus edge.verified / edge.crawler / edge.script / edge.browser. One
spelling instead of one per site owner, and it survives us renaming a header. The
raw-header form still works and has a test pinning that.

ABSENCE IS NOT A CLASS

A rule using this decides whether to serve someone less, so:

- edge.class is null when the validator did not classify, making a comparison
  against it false rather than accidentally true
- a value outside the closed set is DROPPED — outside it means version skew or
  something that is not our worker
- edge.present false means "no information", not "human", and the settings screen
  says so in those words

Derived from the headers already on RuleContext rather than added as a constructor
argument, so a released plugin's RuleContext signature does not change under
anyone.

THE CACHE CAVEAT IS IN THE UI, NOT ONLY THE DOCS

The settings screen now states that these fields are safe for blocking,
throttling, logging and metering, and that serving different page CONTENT on a
cacheable URL is not: Cloudflare's cache key ignores this header below Enterprise,
so the first cached variant is served to everyone including Googlebot, and on a
cache hit the site never runs at all. A site owner reading the field list is
exactly the person about to make that mistake.

No version bump — releasing is a separate decision, and goes through
bin/release-all.sh.

Refs WebDecoy/app#481, WebDecoy/app#477
@cport1
cport1 merged commit da563ca into main Jul 30, 2026
3 checks passed
@cport1
cport1 deleted the feat/read-edge-class-481 branch July 30, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant