@gfargo/doorman 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gfargo/doorman",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Manage firewall rules as code across multiple providers",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1 @@
1
+ v3.1.0
@@ -1,33 +1,47 @@
1
1
  ---
2
2
  name: doorman
3
- description: 'Use when managing Vercel or Cloudflare WAF rules as code, configuring firewall rules, IP blocking, rate limiting, bot protection, or automating multi-provider security configuration deployment with Doorman CLI.'
3
+ description: Manage Vercel and Cloudflare WAF firewall rules as code with the Doorman CLI. Use for any firewall-as-code task — creating rules, IP blocking, rate limiting, bot protection, geo-blocking, syncing local config to providers, validating configurations, exporting documentation, CI/CD automation, or translating rules between Vercel and Cloudflare formats.
4
+ license: MIT
5
+ compatibility: Node.js >= 20. Requires provider API tokens (VERCEL_TOKEN or CLOUDFLARE_API_TOKEN). Install globally via npm install -g @gfargo/doorman.
6
+ metadata:
7
+ author: gfargo
8
+ version: "1.0"
9
+ homepage: https://github.com/gfargo/doorman
10
+ npm: "@gfargo/doorman"
4
11
  ---
5
12
 
6
- # Doorman
13
+ # Doorman — Firewall Rules as Code
7
14
 
8
- Multi-provider WAF automation as code. Manage Vercel Firewall and Cloudflare WAF rules in version-controlled `.doorman.json` files with automated deployment via CLI.
15
+ Doorman is a CLI for managing WAF (Web Application Firewall) rules as code across Vercel and Cloudflare. Configuration lives in `.doorman.json`, syncs bidirectionally with provider APIs, and integrates into CI/CD pipelines.
9
16
 
10
- ## Key Concepts
11
-
12
- - **Config file**: `.doorman.json` (also supports legacy `vercel-firewall.config.json`)
13
- - **Providers**: Vercel (default) and Cloudflare (`--provider cloudflare`)
14
- - **Workflow**: Edit config → validate → diff → sync
15
- - **Schema**: `https://doorman.griffen.codes/schema.json`
16
-
17
- ## Commands
17
+ ## Command Quick Reference
18
18
 
19
19
  ```bash
20
- doorman init --interactive # Create new config
21
- doorman validate # Check config syntax
22
- doorman status # Sync status + health score
23
- doorman list # Show deployed rules
24
- doorman diff # Local vs remote differences
25
- doorman sync # Deploy local config to provider
26
- doorman download # Pull remote rules to local config
27
- doorman template <name> # Add pre-built rule template
28
- doorman watch # Auto-sync on file changes
29
- doorman backup # Create config backup
30
- doorman export --format <fmt> # Export as markdown|json|yaml|terraform
20
+ # Setup & Init
21
+ doorman setup # Show setup guide with links
22
+ doorman init --interactive # Create new config interactively
23
+ doorman init security-focused # Start with security templates
24
+
25
+ # Rule Creation
26
+ doorman add --interactive # Guided rule creation
27
+ doorman add --name "Block" --field path --op pre --value "/admin" --action deny
28
+ doorman template ai-bots # Add pre-built template
29
+
30
+ # Status & Inspection
31
+ doorman status # Sync status + health score
32
+ doorman list # Show deployed rules (table/json)
33
+ doorman diff # Local vs remote differences
34
+
35
+ # Sync & Deploy
36
+ doorman validate # Check config syntax + health
37
+ doorman sync # Deploy local config to provider
38
+ doorman download # Pull remote rules to local config
39
+
40
+ # Advanced
41
+ doorman watch # Auto-sync on file changes
42
+ doorman backup # Create/restore config backups
43
+ doorman export --format markdown # Export as markdown|json|yaml|terraform
44
+ doorman remove --name "Old Rule" # Remove rules by name/ID
31
45
  ```
32
46
 
33
47
  All commands accept `--provider vercel|cloudflare` and `--config <path>`.
@@ -35,15 +49,15 @@ All commands accept `--provider vercel|cloudflare` and `--config <path>`.
35
49
  ## Environment Variables
36
50
 
37
51
  ```bash
38
- # Vercel
52
+ # Vercel (default provider)
39
53
  VERCEL_TOKEN=your_token
40
54
  VERCEL_PROJECT_ID=prj_xxx
41
55
  VERCEL_TEAM_ID=team_xxx
42
56
 
43
- # Cloudflare
57
+ # Cloudflare (beta)
44
58
  CLOUDFLARE_API_TOKEN=your_token
45
59
  CLOUDFLARE_ZONE_ID=zone_xxx
46
- CLOUDFLARE_ACCOUNT_ID=acc_xxx # optional, enables Lists API
60
+ CLOUDFLARE_ACCOUNT_ID=acc_xxx # optional, enables Lists API for bulk IP management
47
61
  ```
48
62
 
49
63
  ## Config Structure
@@ -58,75 +72,58 @@ CLOUDFLARE_ACCOUNT_ID=acc_xxx # optional, enables Lists API
58
72
  }
59
73
  ```
60
74
 
61
- For Cloudflare, use `provider` and `providers` fields instead of `projectId`/`teamId`.
75
+ For Cloudflare, add `provider` and `providers` fields instead of `projectId`/`teamId`.
76
+
77
+ ## Core Workflow
78
+
79
+ ```bash
80
+ # Edit .doorman.json (add/modify rules), then:
81
+ doorman validate && doorman diff && doorman sync
82
+
83
+ # Pull existing rules from a live provider:
84
+ doorman download
85
+
86
+ # Safe production deployment:
87
+ doorman backup && doorman validate && doorman diff && doorman sync && doorman status
88
+ ```
62
89
 
63
- ## Rule Quick Reference
90
+ ## Rule Shape (Minimal)
64
91
 
65
92
  ```json
66
93
  {
67
- "id": "rule_block_admin",
68
- "name": "Block Admin Access",
69
- "description": "Block unauthorized admin access",
94
+ "name": "Block Admin",
70
95
  "active": true,
71
96
  "conditionGroup": [
72
- {
73
- "conditions": [
74
- { "type": "path", "op": "pre", "value": "/admin" },
75
- { "type": "method", "op": "eq", "value": "POST" }
76
- ]
77
- }
97
+ { "conditions": [{ "type": "path", "op": "pre", "value": "/admin" }] }
78
98
  ],
79
- "action": {
80
- "mitigate": {
81
- "action": "deny"
82
- }
83
- }
99
+ "action": { "mitigate": { "action": "deny" } }
84
100
  }
85
101
  ```
86
102
 
87
103
  **Logic**: Conditions within a group are AND'd. Multiple groups are OR'd.
88
104
 
89
- **Condition types**: `path`, `method`, `host`, `user_agent`, `ip_address`, `header`, `query`, `cookie`, `geo_country`, `geo_city`, `geo_continent`, `scheme`
105
+ **Condition types**: `path`, `method`, `host`, `user_agent`, `ip_address`, `header`, `query`, `cookie`, `geo_country`, `geo_city`, `geo_continent`, `geo_country_region`, `geo_as_number`, `scheme`, `protocol`
90
106
 
91
- **Operators**: `eq`, `pre` (starts_with), `suf` (ends_with), `sub` (contains), `inc` (in array), `re` (regex), `ex` (exists), `nex` (not exists)
107
+ **Operators**: `eq`, `pre` (prefix), `suf` (suffix), `sub` (contains), `inc` (in array), `re` (regex), `ex` (exists), `nex` (not exists)
92
108
 
93
109
  **Actions**: `deny`, `challenge`, `rate_limit`, `redirect`, `log`, `bypass`
94
110
 
95
- **See `rules-reference.md` for complete field documentation, advanced patterns, and examples.**
96
-
97
- ## IP Blocking
98
-
99
- ```json
100
- {
101
- "ips": [{ "ip": "192.168.1.0/24", "action": "deny", "hostname": "bad-subnet", "notes": "Blocked for abuse" }]
102
- }
103
- ```
104
-
105
- ## Templates
106
-
107
- ```bash
108
- doorman template bad-bots # Block malicious bots
109
- doorman template ai-bots # Block AI crawlers
110
- doorman template wordpress # Block WordPress attack paths
111
- doorman template block-ofac-sanctioned-countries # OFAC compliance
112
- ```
113
-
114
- ## Workflow
115
-
116
- ```bash
117
- # Add a rule: edit .doorman.json, then:
118
- doorman validate && doorman sync
111
+ ## When to Read Each Reference
119
112
 
120
- # Pull existing rules from provider:
121
- doorman download
113
+ Load the relevant reference file for detailed documentation:
122
114
 
123
- # Check what would change before deploying:
124
- doorman diff
125
- ```
115
+ | Task | Reference |
116
+ |------|-----------|
117
+ | Writing rules — full field docs, operators, actions, IP blocking, patterns | [references/rules.md](references/rules.md) |
118
+ | Cloudflare-specific setup, Lists API, expression translation, limitations | [references/cloudflare.md](references/cloudflare.md) |
119
+ | Available templates and what they protect against | [references/templates.md](references/templates.md) |
120
+ | CI/CD integration, automation, export formats, validation in pipelines | [references/cicd.md](references/cicd.md) |
126
121
 
127
- ## Resources
122
+ ## Principles
128
123
 
129
- - [Docs](https://doorman.griffen.codes/docs)
130
- - [GitHub](https://github.com/gfargo/doorman)
131
- - [Wiki](https://github.com/gfargo/doorman/wiki)
132
- - [Examples](https://github.com/gfargo/doorman/tree/main/examples)
124
+ 1. **Validate before syncing** — always run `doorman validate` before `doorman sync`.
125
+ 2. **Diff before deploying** — use `doorman diff` to preview what will change on the provider.
126
+ 3. **Backup before major changes** — `doorman backup` creates a timestamped snapshot.
127
+ 4. **Config is the source of truth** — make changes in `.doorman.json`, let sync propagate them.
128
+ 5. **Use templates for common patterns** — `doorman template` has battle-tested rules for bots, geo-blocking, and attack paths.
129
+ 6. **Health score matters** — add descriptions, use IDs with `rule_` prefix, avoid regex when simpler operators work.
@@ -0,0 +1,240 @@
1
+ # CI/CD & Automation Reference
2
+
3
+ Integrating Doorman into automated pipelines, export formats, and programmatic usage.
4
+
5
+ ## CI/CD Pipeline Integration
6
+
7
+ Doorman commands return structured output and meaningful exit codes for automation.
8
+
9
+ ### Validation in CI
10
+
11
+ ```bash
12
+ # Fail the build if config is invalid
13
+ doorman validate --config .doorman.json
14
+ # Exit 0 = valid, Exit 1 = validation errors
15
+ ```
16
+
17
+ ### Diff Check (Detect Changes)
18
+
19
+ ```bash
20
+ # Check if local config differs from deployed
21
+ doorman diff --format json --config .doorman.json
22
+ # Exit 0 = differences found (outputs JSON), Exit 1 = error
23
+ ```
24
+
25
+ ### Automated Sync
26
+
27
+ ```bash
28
+ # Deploy rules non-interactively
29
+ doorman sync --config .doorman.json
30
+ ```
31
+
32
+ ### Full Pipeline Example (GitHub Actions)
33
+
34
+ ```yaml
35
+ name: Deploy Firewall Rules
36
+ on:
37
+ push:
38
+ branches: [main]
39
+ paths: ['.doorman.json']
40
+
41
+ jobs:
42
+ deploy:
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+
47
+ - uses: actions/setup-node@v4
48
+ with:
49
+ node-version: '20'
50
+
51
+ - run: npm install -g @gfargo/doorman
52
+
53
+ - name: Validate
54
+ run: doorman validate --config .doorman.json
55
+
56
+ - name: Preview Changes
57
+ run: doorman diff --format json --config .doorman.json || true
58
+
59
+ - name: Deploy
60
+ run: doorman sync --config .doorman.json
61
+ env:
62
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
63
+ VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
64
+ VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
65
+ ```
66
+
67
+ ### Cloudflare Pipeline
68
+
69
+ ```yaml
70
+ - name: Deploy to Cloudflare
71
+ run: doorman sync --provider cloudflare --config .doorman.json
72
+ env:
73
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
74
+ CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
75
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
76
+ ```
77
+
78
+ ## Export Formats
79
+
80
+ ```bash
81
+ doorman export --format json # JSON (default)
82
+ doorman export --format yaml # YAML
83
+ doorman export --format markdown # Human-readable Markdown
84
+ doorman export --format terraform # Terraform HCL
85
+ doorman export --output firewall-docs.md # Write to file
86
+ ```
87
+
88
+ ### JSON Export
89
+
90
+ Full config dump, useful for backup or diffing:
91
+
92
+ ```bash
93
+ doorman export --format json > firewall-backup.json
94
+ ```
95
+
96
+ ### YAML Export
97
+
98
+ Human-friendly format for documentation:
99
+
100
+ ```bash
101
+ doorman export --format yaml --output firewall.yaml
102
+ ```
103
+
104
+ ### Markdown Export
105
+
106
+ Generate team-readable documentation:
107
+
108
+ ```bash
109
+ doorman export --format markdown --output FIREWALL.md
110
+ ```
111
+
112
+ Produces a table of all rules with their conditions, actions, and status.
113
+
114
+ ### Terraform Export
115
+
116
+ Generate Terraform HCL for infrastructure-as-code repositories:
117
+
118
+ ```bash
119
+ doorman export --format terraform --output firewall.tf
120
+ ```
121
+
122
+ ## Backup & Restore
123
+
124
+ ```bash
125
+ # Create a timestamped backup
126
+ doorman backup
127
+
128
+ # List all backups
129
+ doorman backup --list
130
+
131
+ # Restore from a backup
132
+ doorman backup --restore <filename>
133
+ ```
134
+
135
+ Backups are stored locally as JSON files. Include backup creation in your deployment pipeline as a safety net.
136
+
137
+ ## Watch Mode (Development)
138
+
139
+ ```bash
140
+ # Auto-validate and sync on file changes
141
+ doorman watch
142
+
143
+ # Watch a specific config
144
+ doorman watch --config .doorman.json
145
+ ```
146
+
147
+ Watch mode is for development only — it syncs immediately on save. Do not use in production pipelines.
148
+
149
+ ## Programmatic Usage (Node.js)
150
+
151
+ Doorman can be used as a library in Node.js applications:
152
+
153
+ ```typescript
154
+ import { createDoorman } from '@gfargo/doorman/next'
155
+
156
+ // Next.js middleware integration
157
+ const doorman = createDoorman({
158
+ // Configuration options
159
+ })
160
+ ```
161
+
162
+ ## Status & Health Monitoring
163
+
164
+ ```bash
165
+ # Check sync status and configuration health
166
+ doorman status
167
+
168
+ # Machine-readable status
169
+ doorman status --format json
170
+ ```
171
+
172
+ The health score evaluates:
173
+ - Rule descriptions present
174
+ - ID conventions followed (`rule_` prefix)
175
+ - Operator complexity (penalizes unnecessary regex)
176
+ - Active/inactive rule ratio
177
+ - Bot protection coverage
178
+ - Rate limiting coverage
179
+
180
+ ## Multi-Environment Strategies
181
+
182
+ ### Separate configs per environment
183
+
184
+ ```bash
185
+ # Production
186
+ doorman sync --config .doorman.prod.json
187
+
188
+ # Staging (lighter rules)
189
+ doorman sync --config .doorman.staging.json
190
+ ```
191
+
192
+ ### Single config, different providers
193
+
194
+ ```bash
195
+ # Same rules, different targets
196
+ doorman sync --provider vercel --config .doorman.json
197
+ doorman sync --provider cloudflare --config .doorman.json
198
+ ```
199
+
200
+ ### Download from one, deploy to another
201
+
202
+ ```bash
203
+ # Migrate rules between providers
204
+ doorman download --provider vercel --config .doorman.json
205
+ doorman validate --provider cloudflare --config .doorman.json
206
+ doorman sync --provider cloudflare --config .doorman.json
207
+ ```
208
+
209
+ ## Command Flags Reference
210
+
211
+ Global flags available on all commands:
212
+
213
+ | Flag | Description |
214
+ |------|-------------|
215
+ | `--config <path>` | Path to config file (default: auto-discovered `.doorman.json`) |
216
+ | `--provider vercel\|cloudflare` | Override provider detection |
217
+ | `--format json\|table\|yaml\|markdown\|terraform` | Output format (command-dependent) |
218
+ | `--verbose` | Show detailed output |
219
+ | `--output <path>` | Write output to file instead of stdout |
220
+
221
+ ## Error Handling in Automation
222
+
223
+ Doorman exit codes:
224
+ - `0` — success
225
+ - `1` — error (validation failure, API error, config not found)
226
+
227
+ Errors are written to stderr in a structured format. In CI, capture stderr for diagnostics:
228
+
229
+ ```bash
230
+ doorman sync 2> sync-errors.log || { cat sync-errors.log; exit 1; }
231
+ ```
232
+
233
+ ## Security Best Practices for CI/CD
234
+
235
+ 1. **Never commit tokens** — use CI secrets/environment variables exclusively
236
+ 2. **Validate before deploy** — always run `doorman validate` as a gate before `doorman sync`
237
+ 3. **Use `doorman diff`** — review changes in PR checks before merge triggers deployment
238
+ 4. **Backup before sync** — automate `doorman backup` before every `doorman sync` in production
239
+ 5. **Pin the doorman version** — use `npm install -g @gfargo/doorman@3.x` to avoid unexpected breaking changes
240
+ 6. **Limit token scope** — use the narrowest API token permissions possible (project-scoped for Vercel, zone-scoped for Cloudflare)
@@ -0,0 +1,188 @@
1
+ # Cloudflare Provider Reference
2
+
3
+ Cloudflare WAF support in Doorman (beta). Manage Cloudflare custom rulesets and IP Lists through the same config-as-code workflow.
4
+
5
+ ## Setup
6
+
7
+ ### Environment Variables
8
+
9
+ ```bash
10
+ CLOUDFLARE_API_TOKEN=your_token # Required — API token with Zone.Firewall permissions
11
+ CLOUDFLARE_ZONE_ID=zone_xxx # Required — zone to manage rules for
12
+ CLOUDFLARE_ACCOUNT_ID=acc_xxx # Optional — enables Lists API for bulk IP management
13
+ ```
14
+
15
+ ### API Token Permissions
16
+
17
+ Create a Custom Token at https://dash.cloudflare.com/profile/api-tokens with:
18
+ - **Zone > Firewall Services > Edit** — for custom rulesets
19
+ - **Account > Account Filter Lists > Edit** — for Lists API (bulk IP management, requires `CLOUDFLARE_ACCOUNT_ID`)
20
+
21
+ ### Config Shape
22
+
23
+ Multi-provider config with explicit provider declaration:
24
+
25
+ ```json
26
+ {
27
+ "$schema": "https://doorman.griffen.codes/schema.json",
28
+ "provider": "cloudflare",
29
+ "providers": {
30
+ "cloudflare": {
31
+ "zoneId": "your_zone_id",
32
+ "accountId": "your_account_id"
33
+ }
34
+ },
35
+ "rules": [],
36
+ "ips": []
37
+ }
38
+ ```
39
+
40
+ Or pass `--provider cloudflare` to any command to override the default.
41
+
42
+ ## Usage
43
+
44
+ ```bash
45
+ doorman init --provider cloudflare # Initialize Cloudflare config
46
+ doorman validate --provider cloudflare # Validate against Cloudflare constraints
47
+ doorman sync --provider cloudflare # Deploy to Cloudflare
48
+ doorman download --provider cloudflare # Pull rules from Cloudflare
49
+ doorman diff --provider cloudflare # Compare local vs live
50
+ doorman list --provider cloudflare # Show deployed rules
51
+ ```
52
+
53
+ ## Rule Translation
54
+
55
+ Doorman translates its unified rule format into Cloudflare Wirefilter expressions automatically. The translation is bidirectional — `doorman download` pulls Cloudflare expressions back into the unified format.
56
+
57
+ ### Field Mapping
58
+
59
+ | Doorman Type | Cloudflare Field |
60
+ |-------------|------------------|
61
+ | `path` | `http.request.uri.path` |
62
+ | `method` | `http.request.method` |
63
+ | `host` | `http.host` |
64
+ | `user_agent` | `http.user_agent` |
65
+ | `ip_address` | `ip.src` |
66
+ | `header` | `http.request.headers["key"]` |
67
+ | `query` | `http.request.uri.query` |
68
+ | `cookie` | `http.cookie` |
69
+ | `geo_country` | `ip.geoip.country` |
70
+ | `geo_city` | `ip.geoip.city` |
71
+ | `geo_continent` | `ip.geoip.continent` |
72
+ | `geo_country_region` | `ip.geoip.subdivision_1` |
73
+ | `geo_as_number` | `ip.geoip.asnum` |
74
+ | `scheme` | `ssl` (boolean) |
75
+
76
+ ### Action Mapping
77
+
78
+ | Doorman Action | Cloudflare Action |
79
+ |---------------|-------------------|
80
+ | `deny` | `block` |
81
+ | `challenge` | `managed_challenge` |
82
+ | `rate_limit` | `block` + `ratelimit` config |
83
+ | `redirect` | `redirect` + `from_value` params |
84
+ | `log` | `log` |
85
+ | `bypass` | `skip` |
86
+
87
+ ## Lists API (Bulk IP Management)
88
+
89
+ When `CLOUDFLARE_ACCOUNT_ID` is set, IP rules in the `ips` array are managed via Cloudflare's Lists API — a dedicated service for bulk IP blocking that scales better than individual rules.
90
+
91
+ ```bash
92
+ # IPs in config are synced to a Cloudflare List
93
+ doorman sync --provider cloudflare
94
+ ```
95
+
96
+ Without `CLOUDFLARE_ACCOUNT_ID`, IP blocking falls back to individual WAF rules with `ip.src` expressions — functional but limited by rule count quotas.
97
+
98
+ ## Limitations & Differences
99
+
100
+ | Feature | Vercel | Cloudflare | Notes |
101
+ |---------|--------|------------|-------|
102
+ | `re` operator | All plans | Enterprise only | Use `sub`/`pre`/`suf` as alternatives |
103
+ | `environment` type | Yes | No | Vercel-specific concept |
104
+ | `ja3_digest`/`ja4_digest` | Yes | No | Vercel TLS fingerprints |
105
+ | `region` type | Yes | No | Vercel edge region |
106
+ | IP Lists (bulk) | Individual rules | Lists API | Cloudflare needs `accountId` |
107
+ | Max custom rules | ~100 | 5-125 (plan dependent) | Free: 5, Pro: 20, Business: 100, Enterprise: 125+ |
108
+ | Rate limit | Via rule action | Separate phase | Different underlying mechanism |
109
+ | Rule order | Parallel evaluation | Sequential (first match wins) | Ordering matters on Cloudflare |
110
+
111
+ ## Translation Warnings
112
+
113
+ The `RuleTranslator` surfaces warnings when a translation is lossy:
114
+
115
+ - **Regex on non-Enterprise** — `re` operator only works on Cloudflare Enterprise; downgraded to `contains` with a warning
116
+ - **Unsupported types** — Vercel-only fields (`environment`, `ja3_digest`, `ja4_digest`, `region`) are dropped with a warning
117
+ - **Duration differences** — `actionDuration` maps differently between providers
118
+ - **Negation edge cases** — complex negated conditions may produce subtly different behavior in Wirefilter
119
+
120
+ Run `doorman validate --provider cloudflare` to surface warnings before deploying.
121
+
122
+ ## Cloudflare-Specific Validation
123
+
124
+ Doorman validates Cloudflare configs against:
125
+
126
+ - Expression syntax (Wirefilter grammar)
127
+ - Rule count limits per plan
128
+ - Action compatibility (e.g., `js_challenge` deprecated in favor of `managed_challenge`)
129
+ - List reference validity (when using Lists API)
130
+ - Rate limit configuration completeness
131
+
132
+ ## Optimizer
133
+
134
+ The `CloudflareOptimizer` consolidates rules for efficient deployment:
135
+
136
+ - Merges rules with identical actions into single expressions using `or`
137
+ - Deduplicates IP entries across rules and Lists
138
+ - Computes minimal changesets to avoid unnecessary API calls (diff-based sync)
139
+
140
+ ## Example: Full Cloudflare Config
141
+
142
+ ```json
143
+ {
144
+ "$schema": "https://doorman.griffen.codes/schema.json",
145
+ "provider": "cloudflare",
146
+ "providers": {
147
+ "cloudflare": {
148
+ "zoneId": "abc123def456",
149
+ "accountId": "acc789xyz"
150
+ }
151
+ },
152
+ "rules": [
153
+ {
154
+ "id": "rule_block_bots",
155
+ "name": "Block Bad Bots",
156
+ "description": "Block known malicious crawlers",
157
+ "active": true,
158
+ "conditionGroup": [
159
+ { "conditions": [{ "type": "user_agent", "op": "sub", "value": "AhrefsBot" }] },
160
+ { "conditions": [{ "type": "user_agent", "op": "sub", "value": "SemrushBot" }] }
161
+ ],
162
+ "action": { "mitigate": { "action": "deny" } }
163
+ },
164
+ {
165
+ "id": "rule_rate_limit_api",
166
+ "name": "Rate Limit API",
167
+ "description": "Limit API requests to 100/min per IP",
168
+ "active": true,
169
+ "conditionGroup": [
170
+ { "conditions": [{ "type": "path", "op": "pre", "value": "/api/" }] }
171
+ ],
172
+ "action": {
173
+ "mitigate": {
174
+ "action": "rate_limit",
175
+ "rateLimit": {
176
+ "requests": 100,
177
+ "window": "1m",
178
+ "characteristics": ["ip.src"]
179
+ }
180
+ }
181
+ }
182
+ }
183
+ ],
184
+ "ips": [
185
+ { "ip": "203.0.113.0/24", "action": "deny", "notes": "Known attack subnet" }
186
+ ]
187
+ }
188
+ ```