jourilo 1.2.0 → 1.3.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.
@@ -8,7 +8,13 @@ Every command prints human-readable text, or JSON with `--json`. Errors go to st
8
8
  6 stopped early by SIGINT/SIGTERM or `--max-duration` (a partial run is still written; a second signal exits 130).
9
9
 
10
10
  Global options: `--json`, `--workspace DIR` (default `.jourilo`, or `JOURILO_WORKSPACE`), `--quiet`. With `--json`,
11
- progress lines on stderr are JSON too: `{"type":"progress","message":"…","at":"<ISO time>"}`.
11
+ progress lines on stderr are JSON too: `{"type":"progress","message":"…","at":"<ISO time>"}`. Lines from the audit
12
+ pipeline also carry `phase` (launch, discover, setup, capture, metrics, forms or report; `rules` is reserved and not
13
+ emitted yet) and, where known exactly, `done`, `total`, `url` and `viewport`. `done` counts the steps of that phase
14
+ finished before this step started, so the last line of a phase has `done` = `total` − 1; the phase is over when the
15
+ next phase's first line arrives or the run ends. `url` is redacted like the message, and the `message` text is the
16
+ same as the plain line (see docs/RUN-FORMAT.md). `--workspace` is also the output directory: every run is written
17
+ to `DIR/runs/<id>/` (there is no separate `--output-dir`).
12
18
 
13
19
  ## Understand
14
20
 
@@ -17,14 +23,36 @@ progress lines on stderr are JSON too: `{"type":"progress","message":"…","at":
17
23
  Product intelligence: map a repository (frameworks, routes, design system, i18n, auth, analytics) or a website (pages, conversion points, services).
18
24
 
19
25
  ```
20
- jourilo discover <path|url> [--pages 10] [--ignore-robots] [--allow-private]
26
+ jourilo discover <path|url> [--pages 10] [--ignore-robots] [--allow-private] [--deny-local]
21
27
  ```
22
28
 
23
29
  ```
24
30
  Options:
25
- --pages N Pages to read when mapping a website (default 10)
26
- --ignore-robots Ignore robots.txt (only for sites you own or are authorized to test)
27
- --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
31
+ --pages N Pages to read when mapping a website (default 10)
32
+ --ignore-robots Ignore robots.txt (only for sites you own or are authorized to test)
33
+ --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
34
+ --deny-local Refuse local targets (localhost, 127.0.0.1, [::1], *.localhost, *.test) (exit 3); a redirect or request to one is blocked by the network guard and listed in the limitations;
35
+ JOURILO_DENY_LOCAL=1 does the same
36
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
37
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
38
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
39
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
40
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
41
+ loopback connects directly. The run records the proxy without its credentials
42
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
43
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
44
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
45
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
46
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
47
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
48
+ elsewhere (a redirect is followed without them). The run records the names, never the values
49
+ --ignore-https-errors
50
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
51
+ guard still checks every destination, and the run lists it in the limitations
52
+ --chromium-sandbox
53
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
54
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
55
+ where the system denies the sandbox (containers, some Linux CI kernels)
28
56
  ```
29
57
 
30
58
  ## Audit
@@ -34,12 +62,12 @@ Options:
34
62
  Audit a website URL or a static build directory in a real browser (accessibility, UX, responsive, performance, reliability, conversion).
35
63
 
36
64
  ```
37
- jourilo audit <url|dir> [--pages 6] [--viewports desktop,mobile] [--url /path ...] [--repo .] [--fail-on serious]
65
+ jourilo audit <url|dir> [--pages 6] [--viewports desktop,mobile] [--url /path ...] [--repo .] [--fail-on serious] [--budget LCP=2000]
38
66
  ```
39
67
 
40
68
  ```
41
69
  Options:
42
- --pages N Pages to audit after discovery (default 6; 1 = only the target)
70
+ --pages N Pages to audit after discovery (default 6; 1 = only the target; 0 is a usage error)
43
71
  --depth N Link depth for discovery (default 3)
44
72
  --url PATH Audit exactly these paths/URLs on the target origin (repeatable; skips discovery)
45
73
  --include PATH Add pages to discovery (repeatable)
@@ -48,6 +76,8 @@ Options:
48
76
  --headed Show the browser window instead of running headless
49
77
  --locale TAG Browser locale for every page (e.g. tr-TR); sets Accept-Language and navigator.language
50
78
  --timeout SECS Per-page navigation timeout in seconds (default 30)
79
+ --wait-for SELECTOR Capture each page only once this element is visible (client-rendered pages); if it never appears within
80
+ --timeout the page is still audited, with a limitation. Each capture records how it loaded
51
81
  --throttle Simulate a mid-range phone network and 4x CPU slowdown (Chromium)
52
82
  --runs N Visits per page and viewport for lab metrics; reports the median and range (default 1, max 9)
53
83
  --no-keyboard Skip the keyboard-only walk
@@ -61,6 +91,28 @@ Options:
61
91
  --brand SLUG Markilo brand to check design consistency against (default: master brand)
62
92
  --ignore-robots Ignore robots.txt (only for sites you own or are authorized to test)
63
93
  --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
94
+ --deny-local Refuse local targets (localhost, 127.0.0.1, [::1], *.localhost, *.test) (exit 3); a redirect or request to one is blocked by the network guard and listed in the limitations;
95
+ JOURILO_DENY_LOCAL=1 does the same. Not available with --browser webkit (exit 3). For hosted integrations; a directory target is still served
96
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
97
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
98
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
99
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
100
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
101
+ loopback connects directly. The run records the proxy without its credentials
102
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
103
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
104
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
105
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
106
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
107
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
108
+ elsewhere (a redirect is followed without them). The run records the names, never the values
109
+ --ignore-https-errors
110
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
111
+ guard still checks every destination, and the run lists it in the limitations
112
+ --chromium-sandbox
113
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
114
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
115
+ where the system denies the sandbox (containers, some Linux CI kernels)
64
116
  --no-forms Skip the form validation probe
65
117
  (by default, on local hosts, an empty submission is simulated: nothing that sends data
66
118
  leaves the browser, so no database is written; server-side feedback is reported as untested)
@@ -69,10 +121,15 @@ Options:
69
121
  also lets a --setup-journey submit forms
70
122
  --sarif Also write report.sarif for code scanning
71
123
  --fail-on SEVERITY Exit 1 if a defect at or above this severity exists (CI gate)
124
+ --budget LIST Exit 1 when a lab metric exceeds its limit, e.g. LCP=2000,CLS=0.05,TBT=300 (LCP, CLS, INP, FCP,
125
+ TTFB, TBT; ms, CLS unitless). Each capture's median is compared and the worst capture decides;
126
+ a metric no page measured (INP) is reported as not measured, not as a pass. Combines with --fail-on
72
127
  --evidence MODE full (default) or minimal: keep first screens and only the full-page screenshots findings need; JPEG quality 55
73
- --run-id ID Use this run id instead of a generated one (1-64 of A-Z a-z 0-9 _ -); refused if the run exists
74
- --max-duration SECS Stop after this many seconds and write a partial run (exit 6), as SIGINT/SIGTERM do;
75
- a second signal quits at once (exit 130)
128
+ --run-id ID Use this run id instead of a generated one (1-64 of A-Z a-z 0-9 _ -); refused (exit 2) if its
129
+ directory exists, even without a run.json (left by a crashed run)
130
+ --max-duration SECS Stop after this many seconds (1 to 86400) and write a partial run (exit 6), as SIGINT/SIGTERM do;
131
+ the page in progress gets up to 3 s to finish, then it is left out; a second signal quits at
132
+ once (exit 130) after one line on stderr (STOPPED_TWICE with --json)
76
133
  ```
77
134
 
78
135
  ### `jourilo accessibility`
@@ -85,7 +142,7 @@ jourilo accessibility <url|dir> [audit options]
85
142
 
86
143
  ```
87
144
  Options:
88
- --pages N Pages to audit after discovery (default 6; 1 = only the target)
145
+ --pages N Pages to audit after discovery (default 6; 1 = only the target; 0 is a usage error)
89
146
  --depth N Link depth for discovery (default 3)
90
147
  --url PATH Audit exactly these paths/URLs on the target origin (repeatable; skips discovery)
91
148
  --include PATH Add pages to discovery (repeatable)
@@ -94,6 +151,8 @@ Options:
94
151
  --headed Show the browser window instead of running headless
95
152
  --locale TAG Browser locale for every page (e.g. tr-TR); sets Accept-Language and navigator.language
96
153
  --timeout SECS Per-page navigation timeout in seconds (default 30)
154
+ --wait-for SELECTOR Capture each page only once this element is visible (client-rendered pages); if it never appears within
155
+ --timeout the page is still audited, with a limitation. Each capture records how it loaded
97
156
  --throttle Simulate a mid-range phone network and 4x CPU slowdown (Chromium)
98
157
  --runs N Visits per page and viewport for lab metrics; reports the median and range (default 1, max 9)
99
158
  --no-keyboard Skip the keyboard-only walk
@@ -107,6 +166,28 @@ Options:
107
166
  --brand SLUG Markilo brand to check design consistency against (default: master brand)
108
167
  --ignore-robots Ignore robots.txt (only for sites you own or are authorized to test)
109
168
  --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
169
+ --deny-local Refuse local targets (localhost, 127.0.0.1, [::1], *.localhost, *.test) (exit 3); a redirect or request to one is blocked by the network guard and listed in the limitations;
170
+ JOURILO_DENY_LOCAL=1 does the same. Not available with --browser webkit (exit 3). For hosted integrations; a directory target is still served
171
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
172
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
173
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
174
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
175
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
176
+ loopback connects directly. The run records the proxy without its credentials
177
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
178
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
179
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
180
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
181
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
182
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
183
+ elsewhere (a redirect is followed without them). The run records the names, never the values
184
+ --ignore-https-errors
185
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
186
+ guard still checks every destination, and the run lists it in the limitations
187
+ --chromium-sandbox
188
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
189
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
190
+ where the system denies the sandbox (containers, some Linux CI kernels)
110
191
  --no-forms Skip the form validation probe
111
192
  (by default, on local hosts, an empty submission is simulated: nothing that sends data
112
193
  leaves the browser, so no database is written; server-side feedback is reported as untested)
@@ -115,10 +196,15 @@ Options:
115
196
  also lets a --setup-journey submit forms
116
197
  --sarif Also write report.sarif for code scanning
117
198
  --fail-on SEVERITY Exit 1 if a defect at or above this severity exists (CI gate)
199
+ --budget LIST Exit 1 when a lab metric exceeds its limit, e.g. LCP=2000,CLS=0.05,TBT=300 (LCP, CLS, INP, FCP,
200
+ TTFB, TBT; ms, CLS unitless). Each capture's median is compared and the worst capture decides;
201
+ a metric no page measured (INP) is reported as not measured, not as a pass. Combines with --fail-on
118
202
  --evidence MODE full (default) or minimal: keep first screens and only the full-page screenshots findings need; JPEG quality 55
119
- --run-id ID Use this run id instead of a generated one (1-64 of A-Z a-z 0-9 _ -); refused if the run exists
120
- --max-duration SECS Stop after this many seconds and write a partial run (exit 6), as SIGINT/SIGTERM do;
121
- a second signal quits at once (exit 130)
203
+ --run-id ID Use this run id instead of a generated one (1-64 of A-Z a-z 0-9 _ -); refused (exit 2) if its
204
+ directory exists, even without a run.json (left by a crashed run)
205
+ --max-duration SECS Stop after this many seconds (1 to 86400) and write a partial run (exit 6), as SIGINT/SIGTERM do;
206
+ the page in progress gets up to 3 s to finish, then it is left out; a second signal quits at
207
+ once (exit 130) after one line on stderr (STOPPED_TWICE with --json)
122
208
  ```
123
209
 
124
210
  ### `jourilo conversion`
@@ -131,7 +217,7 @@ jourilo conversion <url|dir> [audit options]
131
217
 
132
218
  ```
133
219
  Options:
134
- --pages N Pages to audit after discovery (default 6; 1 = only the target)
220
+ --pages N Pages to audit after discovery (default 6; 1 = only the target; 0 is a usage error)
135
221
  --depth N Link depth for discovery (default 3)
136
222
  --url PATH Audit exactly these paths/URLs on the target origin (repeatable; skips discovery)
137
223
  --include PATH Add pages to discovery (repeatable)
@@ -140,6 +226,8 @@ Options:
140
226
  --headed Show the browser window instead of running headless
141
227
  --locale TAG Browser locale for every page (e.g. tr-TR); sets Accept-Language and navigator.language
142
228
  --timeout SECS Per-page navigation timeout in seconds (default 30)
229
+ --wait-for SELECTOR Capture each page only once this element is visible (client-rendered pages); if it never appears within
230
+ --timeout the page is still audited, with a limitation. Each capture records how it loaded
143
231
  --throttle Simulate a mid-range phone network and 4x CPU slowdown (Chromium)
144
232
  --runs N Visits per page and viewport for lab metrics; reports the median and range (default 1, max 9)
145
233
  --no-keyboard Skip the keyboard-only walk
@@ -153,6 +241,28 @@ Options:
153
241
  --brand SLUG Markilo brand to check design consistency against (default: master brand)
154
242
  --ignore-robots Ignore robots.txt (only for sites you own or are authorized to test)
155
243
  --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
244
+ --deny-local Refuse local targets (localhost, 127.0.0.1, [::1], *.localhost, *.test) (exit 3); a redirect or request to one is blocked by the network guard and listed in the limitations;
245
+ JOURILO_DENY_LOCAL=1 does the same. Not available with --browser webkit (exit 3). For hosted integrations; a directory target is still served
246
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
247
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
248
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
249
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
250
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
251
+ loopback connects directly. The run records the proxy without its credentials
252
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
253
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
254
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
255
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
256
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
257
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
258
+ elsewhere (a redirect is followed without them). The run records the names, never the values
259
+ --ignore-https-errors
260
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
261
+ guard still checks every destination, and the run lists it in the limitations
262
+ --chromium-sandbox
263
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
264
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
265
+ where the system denies the sandbox (containers, some Linux CI kernels)
156
266
  --no-forms Skip the form validation probe
157
267
  (by default, on local hosts, an empty submission is simulated: nothing that sends data
158
268
  leaves the browser, so no database is written; server-side feedback is reported as untested)
@@ -161,10 +271,15 @@ Options:
161
271
  also lets a --setup-journey submit forms
162
272
  --sarif Also write report.sarif for code scanning
163
273
  --fail-on SEVERITY Exit 1 if a defect at or above this severity exists (CI gate)
274
+ --budget LIST Exit 1 when a lab metric exceeds its limit, e.g. LCP=2000,CLS=0.05,TBT=300 (LCP, CLS, INP, FCP,
275
+ TTFB, TBT; ms, CLS unitless). Each capture's median is compared and the worst capture decides;
276
+ a metric no page measured (INP) is reported as not measured, not as a pass. Combines with --fail-on
164
277
  --evidence MODE full (default) or minimal: keep first screens and only the full-page screenshots findings need; JPEG quality 55
165
- --run-id ID Use this run id instead of a generated one (1-64 of A-Z a-z 0-9 _ -); refused if the run exists
166
- --max-duration SECS Stop after this many seconds and write a partial run (exit 6), as SIGINT/SIGTERM do;
167
- a second signal quits at once (exit 130)
278
+ --run-id ID Use this run id instead of a generated one (1-64 of A-Z a-z 0-9 _ -); refused (exit 2) if its
279
+ directory exists, even without a run.json (left by a crashed run)
280
+ --max-duration SECS Stop after this many seconds (1 to 86400) and write a partial run (exit 6), as SIGINT/SIGTERM do;
281
+ the page in progress gets up to 3 s to finish, then it is left out; a second signal quits at
282
+ once (exit 130) after one line on stderr (STOPPED_TWICE with --json)
168
283
  ```
169
284
 
170
285
  ## Journeys
@@ -188,6 +303,28 @@ Suggest options:
188
303
  --pages N Pages to read when deriving journeys from a site (default 8)
189
304
  --force Overwrite journey files that already exist (by default existing files are kept)
190
305
  --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
306
+ --deny-local Refuse local targets (localhost, 127.0.0.1, [::1], *.localhost, *.test) (exit 3); a redirect or request to one is blocked by the network guard and listed in the limitations;
307
+ JOURILO_DENY_LOCAL=1 does the same
308
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
309
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
310
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
311
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
312
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
313
+ loopback connects directly. The run records the proxy without its credentials
314
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
315
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
316
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
317
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
318
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
319
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
320
+ elsewhere (a redirect is followed without them). The run records the names, never the values
321
+ --ignore-https-errors
322
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
323
+ guard still checks every destination, and the run lists it in the limitations
324
+ --chromium-sandbox
325
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
326
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
327
+ where the system denies the sandbox (containers, some Linux CI kernels)
191
328
 
192
329
  Run options:
193
330
  --base URL|DIR The site or static build to run against (required)
@@ -200,7 +337,8 @@ Run options:
200
337
  --allow-submit Allow form submission on non-local hosts (authorized test environments only)
201
338
  --allow-destructive Allow irreversible/payment actions — local hosts with sandbox data only
202
339
  --allow-origin ORIGIN Let the page navigate to another origin you are authorized to test (e.g. a test IdP)
203
- --trace Record a Playwright trace per journey (contains typed values; keep private)
340
+ --trace Record a Playwright trace per journey (contains typed values; keep private);
341
+ refused with --extra-header, since a trace records header values
204
342
  --auth FILE Playwright storage state for signed-in journeys
205
343
  --save-auth FILE After a passing sign-in journey (exactly one), save its session for --auth (test accounts only)
206
344
  --timezone ZONE IANA time zone for the page (e.g. Europe/Istanbul)
@@ -208,6 +346,28 @@ Run options:
208
346
  --evidence MODE full (default) or minimal: keep only the screenshots of the failed step, the step before it and the last step
209
347
  --repeat N Run each journey N times (max 10); mixed outcomes are reported as flaky
210
348
  --allow-private Allow private-network hosts (10.x, 192.168.x, ...)
349
+ --deny-local Refuse local targets (localhost, 127.0.0.1, [::1], *.localhost, *.test) (exit 3); a redirect or request to one is blocked by the network guard and listed in the limitations;
350
+ JOURILO_DENY_LOCAL=1 does the same. Not available with --browser webkit (exit 3)
351
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
352
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
353
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
354
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
355
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
356
+ loopback connects directly. The run records the proxy without its credentials
357
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
358
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
359
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
360
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
361
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
362
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
363
+ elsewhere (a redirect is followed without them). The run records the names, never the values
364
+ --ignore-https-errors
365
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
366
+ guard still checks every destination, and the run lists it in the limitations
367
+ --chromium-sandbox
368
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
369
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
370
+ where the system denies the sandbox (containers, some Linux CI kernels)
211
371
  --fail-on-failure Exit 1 when a journey fails
212
372
 
213
373
  Safety: payment, purchase, delete and cancel actions are never performed on non-local hosts; form submission
@@ -273,6 +433,31 @@ jourilo verify <baseline-run> [--target url|dir] [--fail-on-regression]
273
433
  Options:
274
434
  --target URL|DIR Where to repeat the audit (default: the baseline run's target)
275
435
  --repo PATH Repository for source mapping (default: the baseline run's repo)
436
+ --deny-local Refuse a local target (localhost, 127.0.0.1, [::1], *.localhost, *.test) with exit 3; a redirect to one is blocked by the network guard and listed in the limitations;
437
+ JOURILO_DENY_LOCAL=1 does the same. Not available with --browser webkit (exit 3)
438
+ --proxy URL Chain to an upstream HTTP proxy (http://[user:password@]host:port) behind the network guard;
439
+ default HTTPS_PROXY / HTTP_PROXY (either case; prefer the variable for credentials, argv shows
440
+ in ps). NO_PROXY is decided per destination host (NO_PROXY=* = no proxy). The guard resolves and
441
+ checks every destination itself, then tunnels to the checked address (CONNECT ip:443, and ip:80
442
+ for plain HTTP; a proxy that refuses port 80 fails those requests: give an https:// target);
443
+ loopback connects directly. The run records the proxy without its credentials
444
+ --allow-hosts LIST Only these hosts may be reached (comma-separated or repeated; *.example.com = its subdomains);
445
+ the target's own host is always allowed. Other hosts are refused and listed in the limitations
446
+ --block-hosts LIST Refuse these hosts (e.g. *.tracker.example); refused requests are listed in the limitations
447
+ --user-agent UA Send this User-Agent (also navigator.userAgent) instead of the browser's own; recorded in the run
448
+ --extra-header H Add a request header "Name: value" (repeatable), sent only to the target's origin, never
449
+ to other hosts: Jourilo makes those requests itself, so the headers cannot follow a redirect
450
+ elsewhere (a redirect is followed without them). The run records the names, never the values
451
+ --ignore-https-errors
452
+ Accept invalid or self-signed certificates in the browser (test environments only); the network
453
+ guard still checks every destination, and the run lists it in the limitations
454
+ --chromium-sandbox
455
+ Keep Chromium's own process sandbox (Playwright disables it by default); JOURILO_CHROMIUM_SANDBOX=1
456
+ does the same. Chromium, Chrome and Edge only (a usage error with Firefox or WebKit). Fails to launch
457
+ where the system denies the sandbox (containers, some Linux CI kernels)
458
+ Without --allow-hosts / --block-hosts, the baseline run's lists are used again; so are its
459
+ --user-agent and --ignore-https-errors. Header values are never stored: a baseline that sent
460
+ --extra-header needs the headers again (exit 2 without them)
276
461
  --fail-on-regression Exit 1 when new serious or critical defects appear
277
462
  ```
278
463
 
@@ -394,6 +579,23 @@ Benchmark detection quality on fixture sites with known defects (recall, false p
394
579
  jourilo bench [fixtures-dir=fixtures/sites] [--only name] [--record docs/evaluation/bench-history.jsonl]
395
580
  ```
396
581
 
582
+ ### `jourilo rules`
583
+
584
+ List every rule Jourilo can report, with category, severity and kind (machine-readable with --json).
585
+
586
+ ```
587
+ jourilo rules list [--json]
588
+ ```
589
+
590
+ ```
591
+ Subcommand:
592
+ list Native rules (jourilo/…, perf/…, conv/…, ux/…, a11y/…, journey/…, design/…) and the axe/<id> and
593
+ axe-review/<id> rules a run enables from axe-core 4.14.0. Severity is "varies" when it depends on
594
+ what was measured; kind is "varies" for a rule that reports a defect or, when unsure, a needs-review
595
+ item. With --json: {"jourilo": <version>, "axe": <axe-core version>, "rules": [{id, category,
596
+ severity, kind, title, standards}, …]}.
597
+ ```
598
+
397
599
  ### `jourilo serve`
398
600
 
399
601
  Serve a static build directory on 127.0.0.1 (for audits and journeys of static sites).
@@ -59,6 +59,41 @@
59
59
  },
60
60
  "axe": { "type": ["string", "null"] },
61
61
  "hostClass": { "type": "string" },
62
+ "proxy": {
63
+ "type": "string",
64
+ "description": "Upstream proxy the run chained to behind the network guard (scheme://host:port, never credentials)."
65
+ },
66
+ "allowHosts": {
67
+ "type": "array",
68
+ "items": { "type": "string" },
69
+ "description": "--allow-hosts patterns as given; the target's own host was always allowed."
70
+ },
71
+ "blockHosts": {
72
+ "type": "array",
73
+ "items": { "type": "string" },
74
+ "description": "--block-hosts patterns."
75
+ },
76
+ "userAgent": {
77
+ "type": "string",
78
+ "description": "--user-agent the browser sent instead of its own."
79
+ },
80
+ "extraHeaders": {
81
+ "type": "array",
82
+ "items": { "type": "string" },
83
+ "description": "Names of the --extra-header headers, sent only to the target's origin; their values are never stored."
84
+ },
85
+ "ignoreHttpsErrors": {
86
+ "type": "boolean",
87
+ "description": "--ignore-https-errors: the browser accepted invalid certificates; the network guard still checked every destination."
88
+ },
89
+ "waitFor": {
90
+ "type": "string",
91
+ "description": "--wait-for: each page was captured once this selector was visible (or --timeout passed; see limitations)."
92
+ },
93
+ "chromiumSandbox": {
94
+ "type": "boolean",
95
+ "description": "--chromium-sandbox: Chromium ran with its process sandbox on (Playwright disables it by default)."
96
+ },
62
97
  "scope": {
63
98
  "type": "object",
64
99
  "properties": {
@@ -123,6 +158,36 @@
123
158
  "description": "Why a partial run stopped.",
124
159
  "enum": ["signal", "max-duration"]
125
160
  },
161
+ "budgets": {
162
+ "description": "`--budget` results: per metric, the worst capture's value (the median with --runs N). `ok` is null when no capture measured the metric.",
163
+ "type": "array",
164
+ "items": {
165
+ "type": "object",
166
+ "required": ["metric", "limit", "worst", "url", "viewport", "ok"],
167
+ "properties": {
168
+ "metric": { "type": "string" },
169
+ "limit": { "type": "number" },
170
+ "worst": { "type": ["number", "null"] },
171
+ "url": { "type": ["string", "null"] },
172
+ "viewport": { "type": ["string", "null"] },
173
+ "ok": { "type": ["boolean", "null"] }
174
+ }
175
+ }
176
+ },
177
+ "sample": {
178
+ "description": "Present when discovery found more pages than were audited.",
179
+ "type": "object",
180
+ "required": ["discovered", "audited", "leftOut"],
181
+ "properties": {
182
+ "discovered": { "type": "integer" },
183
+ "audited": { "type": "integer" },
184
+ "leftOut": {
185
+ "description": "At most 20 URLs (redacted) that were discovered but not audited.",
186
+ "type": "array",
187
+ "items": { "type": "string" }
188
+ }
189
+ }
190
+ },
126
191
  "dismissed": {
127
192
  "description": "Findings a person or agent dismissed. Not counted in summary.",
128
193
  "type": "array",
@@ -180,7 +245,9 @@
180
245
  "type": "object",
181
246
  "required": ["pages", "journeys", "findings", "byCategory", "byKind"],
182
247
  "properties": {
183
- "pages": { "type": "integer" },
248
+ "pages": { "type": "integer", "description": "Page captures (URL x viewport), not distinct URLs." },
249
+ "urls": { "type": "integer", "description": "Distinct page URLs that were audited." },
250
+ "captures": { "type": "integer", "description": "Page captures; equal to `pages`." },
184
251
  "journeys": { "type": "integer" },
185
252
  "findings": { "type": "object", "additionalProperties": { "type": "integer" } },
186
253
  "byCategory": { "type": "object", "additionalProperties": { "type": "integer" } },
@@ -324,10 +391,24 @@
324
391
  "title": { "type": "string" },
325
392
  "viewport": { "type": "string" },
326
393
  "screenshot": { "type": "string", "description": "Path relative to the run directory." },
394
+ "foldScreenshot": {
395
+ "type": "string",
396
+ "description": "First-screen screenshot, relative to the run directory."
397
+ },
327
398
  "loadMs": { "type": "number" },
328
399
  "metrics": { "type": "array", "items": { "$ref": "#/$defs/metric" } },
329
400
  "consoleErrors": { "type": "integer" },
330
401
  "failedRequests": { "type": "integer" },
402
+ "load": {
403
+ "type": "object",
404
+ "description": "How the page was captured. `waitedFor` and `found` are present with --wait-for; `found` false means the element never became visible within --timeout.",
405
+ "required": ["networkIdle"],
406
+ "properties": {
407
+ "networkIdle": { "type": "boolean" },
408
+ "waitedFor": { "type": "string" },
409
+ "found": { "type": "boolean" }
410
+ }
411
+ },
331
412
  "inventory": { "$ref": "#/$defs/inventory" }
332
413
  }
333
414
  },
@@ -527,12 +608,40 @@
527
608
  }
528
609
  },
529
610
  "manifest": {
530
- "description": "manifest.json, written next to run.json: every artifact of the run with its size and sha256.",
611
+ "description": "manifest.json, written next to run.json: every artifact of the run with its size and sha256, and the versions and scope that produced them.",
531
612
  "type": "object",
532
613
  "required": ["schemaVersion", "runId", "files"],
533
614
  "properties": {
534
615
  "schemaVersion": { "const": 1 },
535
616
  "runId": { "type": "string" },
617
+ "tool": {
618
+ "description": "The Jourilo release that made the run (run.tool). Absent in manifests written before 1.3.0.",
619
+ "type": "object",
620
+ "required": ["name", "version"],
621
+ "properties": { "name": { "const": "jourilo" }, "version": { "type": "string" } }
622
+ },
623
+ "browser": {
624
+ "description": "The browser and its version (run.browser).",
625
+ "type": "object",
626
+ "required": ["name", "version"],
627
+ "properties": { "name": { "type": "string" }, "version": { "type": "string" } }
628
+ },
629
+ "axe": {
630
+ "description": "The axe-core version the pages were checked with (settings.axe), or the bundled one when no page was checked.",
631
+ "type": "object",
632
+ "required": ["version"],
633
+ "properties": { "version": { "type": "string" } }
634
+ },
635
+ "scope": {
636
+ "description": "What the run covered: the redacted target, the audited URLs (redacted; settings.scope.urls, else the captured pages) and the viewport names.",
637
+ "type": "object",
638
+ "required": ["target", "urls", "viewports"],
639
+ "properties": {
640
+ "target": { "type": "string" },
641
+ "urls": { "type": "array", "items": { "type": "string" } },
642
+ "viewports": { "type": "array", "items": { "type": "string" } }
643
+ }
644
+ },
536
645
  "files": {
537
646
  "type": "array",
538
647
  "items": {