@gfargo/doorman 3.9.2 → 3.10.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.9.2",
3
+ "version": "3.10.0",
4
4
  "description": "Manage firewall rules as code across multiple providers",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: doorman
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.
3
+ description: Manage Vercel, Cloudflare, and Fastly 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 provider formats.
4
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.
5
+ compatibility: Node.js >= 20. Requires provider API tokens (VERCEL_TOKEN, CLOUDFLARE_API_TOKEN, or FASTLY_API_TOKEN). Install globally via npm install -g @gfargo/doorman.
6
6
  metadata:
7
7
  author: gfargo
8
- version: "1.0"
8
+ version: '1.0'
9
9
  homepage: https://github.com/gfargo/doorman
10
- npm: "@gfargo/doorman"
10
+ npm: '@gfargo/doorman'
11
11
  ---
12
12
 
13
13
  # Doorman — Firewall Rules as Code
14
14
 
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.
15
+ Doorman is a CLI for managing WAF (Web Application Firewall) rules as code across Vercel, Cloudflare, and Fastly Next-Gen WAF. Configuration lives in `.doorman.json`, syncs bidirectionally with provider APIs, and integrates into CI/CD pipelines.
16
16
 
17
17
  ## Command Quick Reference
18
18
 
@@ -44,7 +44,7 @@ doorman export --format markdown # Export as markdown|json|yaml|terraform
44
44
  doorman remove --name "Old Rule" # Remove rules by name/ID
45
45
  ```
46
46
 
47
- All commands accept `--provider vercel|cloudflare` and `--config <path>`.
47
+ All commands accept `--provider vercel|cloudflare|fastly` and `--config <path>`.
48
48
 
49
49
  ## Environment Variables
50
50
 
@@ -58,6 +58,10 @@ VERCEL_TEAM_ID=team_xxx
58
58
  CLOUDFLARE_API_TOKEN=your_token
59
59
  CLOUDFLARE_ZONE_ID=zone_xxx
60
60
  CLOUDFLARE_ACCOUNT_ID=acc_xxx # optional, enables Lists API for bulk IP management
61
+
62
+ # Fastly Next-Gen WAF (beta)
63
+ FASTLY_API_TOKEN=your_token
64
+ FASTLY_WORKSPACE_ID=workspace_xxx
61
65
  ```
62
66
 
63
67
  ## Config Structure
@@ -72,7 +76,7 @@ CLOUDFLARE_ACCOUNT_ID=acc_xxx # optional, enables Lists API for bulk IP manage
72
76
  }
73
77
  ```
74
78
 
75
- For Cloudflare, add `provider` and `providers` fields instead of `projectId`/`teamId`.
79
+ For Cloudflare or Fastly, add `provider` and `providers` fields instead of `projectId`/`teamId`.
76
80
 
77
81
  ## Core Workflow
78
82
 
@@ -93,9 +97,7 @@ doorman backup && doorman validate && doorman diff && doorman sync && doorman st
93
97
  {
94
98
  "name": "Block Admin",
95
99
  "active": true,
96
- "conditionGroup": [
97
- { "conditions": [{ "type": "path", "op": "pre", "value": "/admin" }] }
98
- ],
100
+ "conditionGroup": [{ "conditions": [{ "type": "path", "op": "pre", "value": "/admin" }] }],
99
101
  "action": { "mitigate": { "action": "deny" } }
100
102
  }
101
103
  ```
@@ -112,12 +114,13 @@ doorman backup && doorman validate && doorman diff && doorman sync && doorman st
112
114
 
113
115
  Load the relevant reference file for detailed documentation:
114
116
 
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) |
117
+ | Task | Reference |
118
+ | ------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
119
+ | Writing rules — full field docs, operators, actions, IP blocking, patterns | [references/rules.md](references/rules.md) |
120
+ | Cloudflare-specific setup, Lists API, expression translation, limitations | [references/cloudflare.md](references/cloudflare.md) |
121
+ | Fastly-specific setup, condition/action mapping, rate-limit signal requirement, limitations | [references/fastly.md](references/fastly.md) |
122
+ | Available templates and what they protect against | [references/templates.md](references/templates.md) |
123
+ | CI/CD integration, automation, export formats, validation in pipelines | [references/cicd.md](references/cicd.md) |
121
124
 
122
125
  ## Principles
123
126
 
@@ -170,6 +170,7 @@ doorman status --format json
170
170
  ```
171
171
 
172
172
  The health score evaluates:
173
+
173
174
  - Rule descriptions present
174
175
  - ID conventions followed (`rule_` prefix)
175
176
  - Operator complexity (penalizes unnecessary regex)
@@ -210,17 +211,18 @@ doorman sync --provider cloudflare --config .doorman.json
210
211
 
211
212
  Global flags available on all commands:
212
213
 
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 |
214
+ | Flag | Description |
215
+ | ------------------------------------------------- | -------------------------------------------------------------- |
216
+ | `--config <path>` | Path to config file (default: auto-discovered `.doorman.json`) |
217
+ | `--provider vercel\|cloudflare\|fastly` | Override provider detection |
218
+ | `--format json\|table\|yaml\|markdown\|terraform` | Output format (command-dependent) |
219
+ | `--verbose` | Show detailed output |
220
+ | `--output <path>` | Write output to file instead of stdout |
220
221
 
221
222
  ## Error Handling in Automation
222
223
 
223
224
  Doorman exit codes:
225
+
224
226
  - `0` — success
225
227
  - `1` — error (validation failure, API error, config not found)
226
228
 
@@ -0,0 +1,168 @@
1
+ # Fastly Next-Gen WAF Provider Reference
2
+
3
+ Fastly Next-Gen WAF support in Doorman (beta). Manage Next-Gen WAF (formerly Signal Sciences) workspace rules through the same config-as-code workflow. Classic Fastly VCL services are **not** supported — only the Next-Gen WAF's structured rules API.
4
+
5
+ ## Setup
6
+
7
+ ### Environment Variables
8
+
9
+ ```bash
10
+ FASTLY_API_TOKEN=your_token # Required — Fastly API token
11
+ FASTLY_WORKSPACE_ID=workspace_xxx # Required — Next-Gen WAF workspace to manage rules for
12
+ ```
13
+
14
+ ### API Token Permissions
15
+
16
+ Create a token at https://manage.fastly.com/account/personal/tokens with access to the Next-Gen WAF product for the target workspace.
17
+
18
+ ### Config Shape
19
+
20
+ Multi-provider config with explicit provider declaration:
21
+
22
+ ```json
23
+ {
24
+ "$schema": "https://doorman.griffen.codes/schema.json",
25
+ "provider": "fastly",
26
+ "providers": {
27
+ "fastly": {
28
+ "workspaceId": "your_workspace_id"
29
+ }
30
+ },
31
+ "rules": [],
32
+ "ips": []
33
+ }
34
+ ```
35
+
36
+ Or pass `--provider fastly` to any command to override the default.
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ doorman init --provider fastly # Initialize Fastly config
42
+ doorman validate --provider fastly # Validate against Fastly constraints
43
+ doorman sync --provider fastly # Deploy to Fastly
44
+ doorman download --provider fastly # Pull rules from Fastly
45
+ doorman diff --provider fastly # Compare local vs live
46
+ doorman list --provider fastly # Show deployed rules
47
+ ```
48
+
49
+ ## Rule Translation
50
+
51
+ Doorman translates its unified rule format into Fastly's structured `request`-type rules automatically. The translation is bidirectional — `doorman download` pulls Fastly rules back into the unified format.
52
+
53
+ ### Field Mapping
54
+
55
+ | Doorman Field | Fastly Condition | Notes |
56
+ | ------------- | -------------------------- | ------------------------------------------------------- |
57
+ | `path` | `path` | |
58
+ | `method` | `method` | |
59
+ | `host` | `domain` | |
60
+ | `user_agent` | `user_agent` | |
61
+ | `ip` | `ip` | |
62
+ | `country` | `country` | Country-level only — no region/city/continent condition |
63
+ | `scheme` | `scheme` | |
64
+ | `header` | `request_header` multival | Requires `key` (the header name) |
65
+ | `query` | `query_parameter` multival | Requires `key` (the parameter name) |
66
+ | `cookie` | `request_cookie` multival | Requires `key` (the cookie name) |
67
+
68
+ Fields with no Fastly equivalent (`region`, `city`, `asn`, `referer`, `port`, `target_path`, `environment`, `ja3_digest`, `ja4_digest`) are dropped with a translation warning rather than silently mistranslated — the rule still syncs with its remaining conditions.
69
+
70
+ ### Operator Mapping
71
+
72
+ | Doorman Operator | Fastly Operator | Notes |
73
+ | -------------------------- | ------------------------------------------- | -------------------------------------------------------------- |
74
+ | `eq` | `equals` (or `does_not_equal` if `negated`) | |
75
+ | `contains` | `contains` (or `does_not_contain`) | |
76
+ | `matches` | `matches` (or `does_not_match`) | Regex syntax may need adjustment |
77
+ | `starts_with`, `ends_with` | `like` (or `not_like`) | Fastly's wildcard matching isn't a true prefix/suffix operator |
78
+ | `in` | `in_list` (or `not_in_list`) | |
79
+ | `gt`, `lt` | `greater_equal`, `lesser_equal` | No strict `>`/`<` on Fastly — boundary value also matches |
80
+
81
+ ### Action Mapping
82
+
83
+ | Doorman Action | Fastly Action | Notes |
84
+ | --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------- |
85
+ | `block`, `deny` | `block` | |
86
+ | `allow` | `allow` | |
87
+ | `challenge` | `browser_challenge` | |
88
+ | `redirect` | `redirect` | Uses `redirect_url` + `response_code` |
89
+ | `rate_limit` | Rule `type: rate_limit` + `block_signal` action | See Rate Limiting below |
90
+ | `log` | `allow` (with a warning) | Fastly has no dedicated log-only action; `request_logging` is set at the rule level regardless |
91
+ | `bypass` | `allow` (with a warning) | No equivalent action |
92
+
93
+ ## Condition Grouping
94
+
95
+ Fastly's condition model is bounded to two levels — a rule's top-level conditions can be plain, or grouped, but a group's own members can never contain another group. This matches Doorman's flat `group` model exactly:
96
+
97
+ - Conditions sharing a `group` index are AND'd together.
98
+ - Distinct `group` values are OR'd against each other.
99
+ - A single implicit group (the common case) becomes flat top-level conditions.
100
+
101
+ ## Rate Limiting
102
+
103
+ Fastly rate-limit rules are a distinct rule type (`type: "rate_limit"`) with their own `rate_limit` block (`threshold`, `interval`, `duration`, a counting `signal`). Doorman's `window` (e.g. `"60s"`, `"5m"`) is rounded to the nearest interval Fastly supports (60s / 600s / 3600s).
104
+
105
+ **Fastly rate-limit rules count requests against a named custom signal that must already exist in the workspace** — Doorman does not create it automatically. Create a signal named `doorman-rate-limit-<rule-id>` before syncing a rate-limit rule, or the sync will be rejected. A translation warning is emitted every time to make this easy to miss less easily.
106
+
107
+ ## IP Blocking
108
+
109
+ Fastly has no per-IP-rule resource — Doorman manages two workspace **lists** of type `ip` (`doorman-managed-deny`, `doorman-managed-allow`), each replaced wholesale on sync (the same atomic-replace shape as Cloudflare's ruleset write, not Vercel's per-item writes). `hostname`/`notes` on an IP rule have no Fastly equivalent and are dropped with a warning — only the address is kept.
110
+
111
+ ## No Draft/Publish Step
112
+
113
+ Unlike classic Fastly VCL services, Next-Gen WAF rule and list writes take effect immediately — there is no draft-then-activate version to manage. A rule's `enabled` flag is the only gate on whether it's live.
114
+
115
+ ## Limitations & Differences
116
+
117
+ | Feature | Vercel | Fastly | Notes |
118
+ | -------------------------- | ----------------------------- | ---------------------- | ------------------------------------------------------------------------- |
119
+ | Geo targeting | Country/city/continent/region | Country only | No sub-country condition field |
120
+ | Rule ordering | Best-effort (insertion order) | None | Next-Gen WAF rules are evaluated independently — `priority` has no effect |
121
+ | Managed/vendor rule groups | CRS (enterprise) | Templated signal rules | Not yet configurable through Doorman for either provider (#183) |
122
+ | Signal/exclusion rules | — | `type: signal` | Not managed by Doorman — see below |
123
+
124
+ Fastly rules of type `signal` or `templated_signal` (which tag or exclude WAF signals rather than allow/block/redirect a request) are skipped entirely by `doorman download`/`fetchConfig` rather than forced into a lossy `UnifiedRule` — they simply don't appear in the local config.
125
+
126
+ ## Example: Full Fastly Config
127
+
128
+ ```json
129
+ {
130
+ "$schema": "https://doorman.griffen.codes/schema.json",
131
+ "provider": "fastly",
132
+ "providers": {
133
+ "fastly": {
134
+ "workspaceId": "abc123def456"
135
+ }
136
+ },
137
+ "rules": [
138
+ {
139
+ "id": "rule_block_bots",
140
+ "name": "Block Bad Bots",
141
+ "enabled": true,
142
+ "conditions": [
143
+ { "field": "user_agent", "operator": "contains", "value": "AhrefsBot", "group": 0 },
144
+ { "field": "user_agent", "operator": "contains", "value": "SemrushBot", "group": 1 }
145
+ ],
146
+ "action": { "type": "block" }
147
+ },
148
+ {
149
+ "id": "rule_admin_header",
150
+ "name": "Require internal header on admin paths",
151
+ "enabled": true,
152
+ "conditions": [
153
+ { "field": "path", "operator": "starts_with", "value": "/admin", "group": 0 },
154
+ {
155
+ "field": "header",
156
+ "operator": "eq",
157
+ "value": "internal",
158
+ "key": "X-Access-Level",
159
+ "negated": true,
160
+ "group": 0
161
+ }
162
+ ],
163
+ "action": { "type": "block" }
164
+ }
165
+ ],
166
+ "ips": [{ "ip": "203.0.113.0/24", "action": "deny" }]
167
+ }
168
+ ```
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "security",
3
- "description": "Web application firewall management as code with Doorman: create rules, block IPs, rate limit, protect against bots, and deploy WAF configs to Vercel and Cloudflare.",
3
+ "description": "Web application firewall management as code with Doorman: create rules, block IPs, rate limit, protect against bots, and deploy WAF configs to Vercel, Cloudflare, and Fastly.",
4
4
  "version": "1.0.0",
5
5
  "author": {
6
6
  "name": "Griffen Fargo",
@@ -13,6 +13,7 @@
13
13
  "waf",
14
14
  "vercel",
15
15
  "cloudflare",
16
+ "fastly",
16
17
  "doorman",
17
18
  "iac",
18
19
  "rules",