ax-audit 3.1.0 → 4.0.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/CHANGELOG.md +198 -0
- package/LICENSE +1 -1
- package/README.md +89 -231
- package/dist/baseline.d.ts +2 -0
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +42 -4
- package/dist/baseline.js.map +1 -1
- package/dist/check-ids.d.ts +19 -0
- package/dist/check-ids.d.ts.map +1 -0
- package/dist/check-ids.js +53 -0
- package/dist/check-ids.js.map +1 -0
- package/dist/checks/agent-access.d.ts +33 -0
- package/dist/checks/agent-access.d.ts.map +1 -0
- package/dist/checks/agent-access.js +256 -0
- package/dist/checks/agent-access.js.map +1 -0
- package/dist/checks/agent-card.d.ts +37 -0
- package/dist/checks/agent-card.d.ts.map +1 -0
- package/dist/checks/agent-card.js +352 -0
- package/dist/checks/agent-card.js.map +1 -0
- package/dist/checks/agent-operability.d.ts +66 -0
- package/dist/checks/agent-operability.d.ts.map +1 -0
- package/dist/checks/agent-operability.js +383 -0
- package/dist/checks/agent-operability.js.map +1 -0
- package/dist/checks/agent-skills.d.ts +24 -0
- package/dist/checks/agent-skills.d.ts.map +1 -0
- package/dist/checks/agent-skills.js +316 -0
- package/dist/checks/agent-skills.js.map +1 -0
- package/dist/checks/ai-catalog.d.ts +28 -0
- package/dist/checks/ai-catalog.d.ts.map +1 -0
- package/dist/checks/ai-catalog.js +254 -0
- package/dist/checks/ai-catalog.js.map +1 -0
- package/dist/checks/ai-directives.d.ts +57 -0
- package/dist/checks/ai-directives.d.ts.map +1 -0
- package/dist/checks/ai-directives.js +263 -0
- package/dist/checks/ai-directives.js.map +1 -0
- package/dist/checks/api-discovery.d.ts +26 -0
- package/dist/checks/api-discovery.d.ts.map +1 -0
- package/dist/checks/api-discovery.js +432 -0
- package/dist/checks/api-discovery.js.map +1 -0
- package/dist/checks/auth-discovery.d.ts +28 -0
- package/dist/checks/auth-discovery.d.ts.map +1 -0
- package/dist/checks/auth-discovery.js +213 -0
- package/dist/checks/auth-discovery.js.map +1 -0
- package/dist/checks/commerce-discovery.d.ts +40 -0
- package/dist/checks/commerce-discovery.d.ts.map +1 -0
- package/dist/checks/commerce-discovery.js +295 -0
- package/dist/checks/commerce-discovery.js.map +1 -0
- package/dist/checks/content-negotiation.d.ts.map +1 -1
- package/dist/checks/content-negotiation.js +135 -20
- package/dist/checks/content-negotiation.js.map +1 -1
- package/dist/checks/crawl-efficiency.d.ts +16 -0
- package/dist/checks/crawl-efficiency.d.ts.map +1 -0
- package/dist/checks/crawl-efficiency.js +186 -0
- package/dist/checks/crawl-efficiency.js.map +1 -0
- package/dist/checks/frontmatter.d.ts +34 -0
- package/dist/checks/frontmatter.d.ts.map +1 -0
- package/dist/checks/frontmatter.js +100 -0
- package/dist/checks/frontmatter.js.map +1 -0
- package/dist/checks/html-rendering.d.ts.map +1 -1
- package/dist/checks/html-rendering.js +0 -1
- package/dist/checks/html-rendering.js.map +1 -1
- package/dist/checks/html-utils.d.ts +10 -0
- package/dist/checks/html-utils.d.ts.map +1 -1
- package/dist/checks/html-utils.js +19 -0
- package/dist/checks/html-utils.js.map +1 -1
- package/dist/checks/http-headers.d.ts.map +1 -1
- package/dist/checks/http-headers.js +82 -10
- package/dist/checks/http-headers.js.map +1 -1
- package/dist/checks/http-hygiene.d.ts +26 -0
- package/dist/checks/http-hygiene.d.ts.map +1 -0
- package/dist/checks/http-hygiene.js +257 -0
- package/dist/checks/http-hygiene.js.map +1 -0
- package/dist/checks/index.d.ts.map +1 -1
- package/dist/checks/index.js +30 -8
- package/dist/checks/index.js.map +1 -1
- package/dist/checks/llms-txt.d.ts +15 -0
- package/dist/checks/llms-txt.d.ts.map +1 -1
- package/dist/checks/llms-txt.js +162 -2
- package/dist/checks/llms-txt.js.map +1 -1
- package/dist/checks/mcp-discovery.d.ts +30 -0
- package/dist/checks/mcp-discovery.d.ts.map +1 -0
- package/dist/checks/mcp-discovery.js +523 -0
- package/dist/checks/mcp-discovery.js.map +1 -0
- package/dist/checks/meta-tags.d.ts.map +1 -1
- package/dist/checks/meta-tags.js +6 -5
- package/dist/checks/meta-tags.js.map +1 -1
- package/dist/checks/robots-parser.d.ts +110 -0
- package/dist/checks/robots-parser.d.ts.map +1 -0
- package/dist/checks/robots-parser.js +277 -0
- package/dist/checks/robots-parser.js.map +1 -0
- package/dist/checks/robots-txt.d.ts +2 -0
- package/dist/checks/robots-txt.d.ts.map +1 -1
- package/dist/checks/robots-txt.js +252 -45
- package/dist/checks/robots-txt.js.map +1 -1
- package/dist/checks/{mcp.d.ts → rsl.d.ts} +1 -1
- package/dist/checks/rsl.d.ts.map +1 -0
- package/dist/checks/rsl.js +242 -0
- package/dist/checks/rsl.js.map +1 -0
- package/dist/checks/security-txt.d.ts.map +1 -1
- package/dist/checks/security-txt.js +0 -1
- package/dist/checks/security-txt.js.map +1 -1
- package/dist/checks/seo-basics.d.ts.map +1 -1
- package/dist/checks/seo-basics.js +0 -1
- package/dist/checks/seo-basics.js.map +1 -1
- package/dist/checks/sitemap.d.ts.map +1 -1
- package/dist/checks/sitemap.js +0 -1
- package/dist/checks/sitemap.js.map +1 -1
- package/dist/checks/structured-data.d.ts.map +1 -1
- package/dist/checks/structured-data.js +215 -4
- package/dist/checks/structured-data.js.map +1 -1
- package/dist/checks/structured-fields.d.ts +46 -0
- package/dist/checks/structured-fields.d.ts.map +1 -0
- package/dist/checks/structured-fields.js +112 -0
- package/dist/checks/structured-fields.js.map +1 -0
- package/dist/checks/surface.d.ts +59 -0
- package/dist/checks/surface.d.ts.map +1 -0
- package/dist/checks/surface.js +106 -0
- package/dist/checks/surface.js.map +1 -0
- package/dist/checks/tls-https.d.ts.map +1 -1
- package/dist/checks/tls-https.js +0 -1
- package/dist/checks/tls-https.js.map +1 -1
- package/dist/checks/usage-policy.d.ts +53 -0
- package/dist/checks/usage-policy.d.ts.map +1 -0
- package/dist/checks/usage-policy.js +339 -0
- package/dist/checks/usage-policy.js.map +1 -0
- package/dist/checks/utils.d.ts +25 -1
- package/dist/checks/utils.d.ts.map +1 -1
- package/dist/checks/utils.js +33 -1
- package/dist/checks/utils.js.map +1 -1
- package/dist/checks/waf.d.ts +75 -0
- package/dist/checks/waf.d.ts.map +1 -0
- package/dist/checks/waf.js +203 -0
- package/dist/checks/waf.js.map +1 -0
- package/dist/checks/webmcp.d.ts +55 -0
- package/dist/checks/webmcp.d.ts.map +1 -0
- package/dist/checks/webmcp.js +209 -0
- package/dist/checks/webmcp.js.map +1 -0
- package/dist/checks/well-known.d.ts +38 -0
- package/dist/checks/well-known.d.ts.map +1 -0
- package/dist/checks/well-known.js +202 -0
- package/dist/checks/well-known.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +144 -6
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +212 -14
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +625 -59
- package/dist/constants.js.map +1 -1
- package/dist/fetcher.d.ts +5 -1
- package/dist/fetcher.d.ts.map +1 -1
- package/dist/fetcher.js +62 -27
- package/dist/fetcher.js.map +1 -1
- package/dist/guide-urls.js +1 -1
- package/dist/guide-urls.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +2 -2
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +18 -7
- package/dist/orchestrator.js.map +1 -1
- package/dist/reporter/html.d.ts +9 -0
- package/dist/reporter/html.d.ts.map +1 -1
- package/dist/reporter/html.js +49 -11
- package/dist/reporter/html.js.map +1 -1
- package/dist/reporter/index.d.ts.map +1 -1
- package/dist/reporter/index.js +7 -0
- package/dist/reporter/index.js.map +1 -1
- package/dist/reporter/markdown.d.ts +8 -0
- package/dist/reporter/markdown.d.ts.map +1 -0
- package/dist/reporter/markdown.js +106 -0
- package/dist/reporter/markdown.js.map +1 -0
- package/dist/reporter/terminal.d.ts.map +1 -1
- package/dist/reporter/terminal.js +36 -1
- package/dist/reporter/terminal.js.map +1 -1
- package/dist/scorer.d.ts +10 -0
- package/dist/scorer.d.ts.map +1 -1
- package/dist/scorer.js +22 -5
- package/dist/scorer.js.map +1 -1
- package/dist/types.d.ts +96 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/api.md +200 -0
- package/docs/architecture.md +104 -0
- package/docs/checks.md +555 -0
- package/docs/ci.md +89 -0
- package/docs/cli.md +103 -0
- package/docs/concepts.md +101 -0
- package/docs/faq.md +89 -0
- package/docs/getting-started.md +108 -0
- package/docs/roadmap.md +367 -0
- package/package.json +14 -5
- package/dist/checks/agent-json.d.ts +0 -14
- package/dist/checks/agent-json.d.ts.map +0 -1
- package/dist/checks/agent-json.js +0 -167
- package/dist/checks/agent-json.js.map +0 -1
- package/dist/checks/mcp.d.ts.map +0 -1
- package/dist/checks/mcp.js +0 -162
- package/dist/checks/mcp.js.map +0 -1
- package/dist/checks/openapi.d.ts +0 -4
- package/dist/checks/openapi.d.ts.map +0 -1
- package/dist/checks/openapi.js +0 -121
- package/dist/checks/openapi.js.map +0 -1
- package/dist/checks/well-known-ai.d.ts +0 -17
- package/dist/checks/well-known-ai.d.ts.map +0 -1
- package/dist/checks/well-known-ai.js +0 -123
- package/dist/checks/well-known-ai.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,204 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to ax-audit are documented here.
|
|
4
4
|
|
|
5
|
+
## [4.0.0] - 2026-09-04
|
|
6
|
+
|
|
7
|
+
**Scores change on every site.** This release redistributes the weights, makes protocol checks conditional, and retires a check whose premise did not survive verification. Baselines written by earlier versions are read normally, but regression gating is suspended across the change — a rescore is not something the site did.
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- **Weights redistributed by what actually stops an agent.** Content leads at 33, because the failure that breaks the most agents is a page with nothing in its HTML: most crawlers do not run JavaScript, so content that appears only after hydration is invisible no matter how many discovery files a site publishes. `html-rendering` is the highest-weighted check at 11. Access is second at 24, because a firewall rule or a `nosnippet` directive silently undoes everything else. Discovery drops to 21, with llms.txt demoted from 11 to 5 on the evidence that most published files are never fetched by an AI search crawler.
|
|
12
|
+
- **Protocol checks are conditional.** A blog has no API to describe and is not an agent; scoring it zero said something false and made the overall number unreadable. Each protocol check now detects whether the site has the corresponding surface, reports **n/a** when it does not, and leaves the denominator. When the surface *is* present, a missing description is a stronger finding than before, and the wording says so: not "no MCP server discovery found" but "MCP server present but not discoverable".
|
|
13
|
+
- **`well-known-ai` retired.** Three of its five scored files had no consumer: `nlweb.json` appears in no NLWeb release, `genai.txt` has no specification, and `ai-plugin.json` described a product shut down in 2024. Its useful probes moved into the checks that own them in 3.8.
|
|
14
|
+
- **The 3.x scoring freezes are gone.** `robots-txt` scores against all twelve current core crawlers rather than the frozen eight, so the configuration that scored 100 in 3.6 now scores 90. Blocking a crawler deducts by purpose: 5 for a search crawler, whose absence removes the site from that assistant's answers, and 2 for a training crawler, where blocking is a defensible policy choice.
|
|
15
|
+
- **`CheckMeta.weight` is now optional** and nothing uses it. Weights live in `CHECK_WEIGHTS` alone. Having them in two places is what let them drift.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **`--profile api|mcp|agent|docs|commerce|all`** forces conditional checks applicable, for auditing against what a site intends to build rather than what it already has.
|
|
20
|
+
- **`--category content|discovery|access|policy|protocols`** runs one area. It narrows an explicit `--checks` selection rather than replacing it.
|
|
21
|
+
- **`--fail-on-category access:70,content:80`** gates CI per area. An overall score hides an area that is entirely broken: a site can score 80 while every access check fails because the other four carry it. Areas with nothing applicable are reported as not evaluated rather than failed.
|
|
22
|
+
- **Versioned baselines.** `schemaVersion` records which scoring model produced a baseline; a mismatch shows the deltas but suspends gating and says how to resume it. Baselines also record which checks were N/A, so gaining or losing a surface is not read as a regression or an improvement.
|
|
23
|
+
|
|
24
|
+
### Migration
|
|
25
|
+
|
|
26
|
+
1. Expect a different score. Sites with no API or MCP surface generally rise, because they are no longer marked down for lacking things they do not have. A bare page rose from 20 to 50 in testing; a documentation site with real infrastructure rose from 64 to 77.
|
|
27
|
+
2. Re-save your baseline: `ax-audit <url> --save-baseline .ax-baseline.json`. Until you do, `--fail-on-regression` reports but does not gate.
|
|
28
|
+
3. `--checks agent-json`, `--checks mcp` and `--checks openapi` still work; the ids were renamed in 3.7 and the old names remain aliases.
|
|
29
|
+
4. If you relied on `well-known-ai`, its live probes are now in `usage-policy` (TDMRep) and the checks that own each file.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **JSON-LD was missed when `type` was not the first attribute** on the script tag. Next.js and several other frameworks emit `<script id="…" type="application/ld+json">`, so a large class of well-marked-up sites scored 0 on a 6-weight check and were told to add markup they already had. Three copies of the pattern now share one order-independent extractor.
|
|
34
|
+
|
|
35
|
+
### Tests
|
|
36
|
+
|
|
37
|
+
873 total. New coverage for surface detection, the weight distribution (sums to 100, no check declares its own, content outweighs every area), the CLI end to end via a subprocess, and baseline versioning in both directions.
|
|
38
|
+
|
|
39
|
+
## [3.9.0] - 2026-09-04
|
|
40
|
+
|
|
41
|
+
Depth rather than breadth. One new check, and the existing readability checks extended to cover what agents actually do with a page once they reach it. Everything added is informational; scores are unchanged.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **agent-operability**: whether a browser agent can work the page or only look at it. Every major browser agent reads the accessibility tree, so a `<div onclick>` styled as a button does not appear at all — the agent does not see a button it cannot press, it sees nothing. Checks accessible names on buttons and links, labels on form controls, elements dressed as controls, dead links, table headers, iframe titles, `<time datetime>`, heading skips, unsized media, `<html lang>`, and entry-page obstacles. Reports proportions with a 90% threshold, and states on every run that it reads markup rather than a rendered tree.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **llms.txt** gained the 2026 specification revision's additions — subpath scoping via `rel="describedby"`, per-page `.md` mirrors — plus link-health sampling. An llms.txt is written once and never revalidated while the site moves underneath it, so an index of dead links is its characteristic failure, and it wastes exactly the budget the file exists to save. Dead links are reported separately from redirecting ones. Every report now states who actually reads this file.
|
|
50
|
+
- **content-negotiation** stopped probing with a header no client sends. A bare `Accept: text/markdown` would pass against an implementation that fails every real request; the probe now sends what Claude Code, Cursor and OpenCode send. Added: origin-reported token counts preferred over a byte ratio, frontmatter validation (stripping the HTML also strips the canonical link, leaving an agent unable to attribute what it quotes), and probes for user-agent negotiation and `.md` suffix URLs before concluding a site has no Markdown.
|
|
51
|
+
- **structured-data** now reads provenance and freshness — `author`, `sameAs`, `publisher`, `dateModified` — and compares `headline` and `name` against the visible text, which is Google's one explicit requirement for structured data and AI features.
|
|
52
|
+
- **crawl-efficiency** reports a page's cost in tokens rather than bytes, naming the markup share an agent pays to receive and then discards, plus response time in agent terms.
|
|
53
|
+
|
|
54
|
+
### Tests
|
|
55
|
+
|
|
56
|
+
828 total, up from 777.
|
|
57
|
+
|
|
58
|
+
## [3.8.0] - 2026-09-04
|
|
59
|
+
|
|
60
|
+
Eight new checks, all weight 0, plus the reporting machinery they need. Where 3.7 corrected what ax-audit was looking for, 3.8 covers the signals it was not looking at.
|
|
61
|
+
|
|
62
|
+
### Added — reporting
|
|
63
|
+
|
|
64
|
+
- **Not-applicable checks.** A commerce-protocol check scoring zero on a personal blog says the blog is badly built; the honest answer is that the question does not arise. `CheckResult.applicable: false` excludes a check from both numerator and denominator, and reports render it as `n/a`. Everything counted against a site is now something the site could have done. A check whose meta exists but produced no result — because it crashed — still counts at full weight, so a broken check cannot inflate a score.
|
|
65
|
+
- **Category grouping.** Terminal, Markdown and HTML output group checks under content, discovery, access, policy and protocols. Twenty-six scores in a flat list do not tell a reader where the problem is.
|
|
66
|
+
- **`renderHtml` / `renderBatchHtml`**, returning the document as a string the way `renderMarkdown` already did. The HTML reporter could previously only print to stdout, which made it untestable and unusable from the programmatic API.
|
|
67
|
+
|
|
68
|
+
### Added — checks
|
|
69
|
+
|
|
70
|
+
- **ai-directives**: the page-level controls Google and Microsoft document that they honor — `nosnippet` and `max-snippet` for AI Overviews and AI Mode, `noarchive` and `nocache` for Copilot grounding, `data-nosnippet` judged by what it wraps. The finding it exists for: a site that disallows `Google-Extended` expecting to leave AI Overviews has done no such thing, because that token governs Gemini training and grounding while AI Overviews follow Googlebot and the snippet directives.
|
|
71
|
+
- **usage-policy**: normalises Content Signals, AIPREF, RSL, TDMRep and `noai` onto three questions — train, ground, index — and reports where they disagree. A site whose robots.txt permits training while its RSL licence prohibits it has published two contradictory positions, and which one applies depends on which file a crawler read. Every report states that only robots.txt access rules are documented as honored by major operators.
|
|
72
|
+
- **http-hygiene**: status-code honesty. An agent cannot see that the 200 it received is a "page not found" screen; it stores the apology as the answer. Also redirect depth, `HEAD` support, `Retry-After` on 429, charset, and `<html lang>` against `Content-Language`.
|
|
73
|
+
- **ai-catalog**: the emerging single index of everything callable on a site, discovered the four ways Lighthouse's own audit accepts. Both specifications are drafts, so absence never scores — but a catalog whose entries point at documents that 404 does, because an agent trusts it before it fetches.
|
|
74
|
+
- **agent-skills**: installable procedures (SKILL.md) across all three competing discovery paths, validating the index and fetching a sample of the documents. Conditional on the site having a developer-facing surface.
|
|
75
|
+
- **webmcp**: forms declared as callable tools. Never asks for WebMCP, which is a Community Group draft in a Chrome origin trial; catches the mistakes static analysis can catch with certainty on pages that adopted it.
|
|
76
|
+
- **commerce-discovery**: the Universal Commerce Protocol profile, the one agentic-commerce specification with published site-side discovery. Validates the profile and resolves the schema URLs it promises. The alternatives define no manifest, and the report says so rather than inventing paths.
|
|
77
|
+
- **auth-discovery**: the RFC 9728 chain from `WWW-Authenticate` to the authorization server's metadata. A human hitting a 401 reads the documentation; an agent cannot.
|
|
78
|
+
|
|
79
|
+
### Added — infrastructure
|
|
80
|
+
|
|
81
|
+
- `checks/frontmatter.ts`: a focused YAML frontmatter reader for SKILL.md and Markdown mirrors. ax-audit has two runtime dependencies and will not grow a YAML parser, so the reader handles the flat `key: value` block these documents use and records anything more elaborate as skipped rather than guessing.
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
|
|
85
|
+
- **A lone `Offer` is a price, not a storefront.** Found by running commerce-discovery against a real SaaS site, which was told to build a commerce integration because it prices its plans with structured data. A bare `Offer` now counts only alongside a second signal such as a cart link.
|
|
86
|
+
- `buildResult` falls back to `CHECK_CATEGORIES`, so every result carries its category in JSON output too. Previously only checks setting `meta.category` explicitly did, leaving JSON consumers to reimplement the fallback the other reporters already had.
|
|
87
|
+
|
|
88
|
+
### Scoring
|
|
89
|
+
|
|
90
|
+
Unchanged. All eight checks carry weight 0 and gain weight in 4.0.
|
|
91
|
+
|
|
92
|
+
### Tests
|
|
93
|
+
|
|
94
|
+
755 total, up from 553. New suites: ai-directives, usage-policy, http-hygiene, ai-catalog, agent-skills, webmcp, commerce-discovery, auth-discovery, frontmatter, and the N/A arithmetic including the crashed-check case.
|
|
95
|
+
|
|
96
|
+
## [3.7.0] - 2026-09-04
|
|
97
|
+
|
|
98
|
+
A correction release. Three checks were probing paths that are no longer, or never were, the standard, and the crawler catalogue had drifted far enough to contain tokens that do not exist. Everything here was re-verified against vendor documentation, IANA, IETF datatracker and the relevant specification repositories on 2026-09-04.
|
|
99
|
+
|
|
100
|
+
**No score goes down.** Every correction that would have lowered an existing score is frozen behind a constant that is removed in 4.0, and tests assert it.
|
|
101
|
+
|
|
102
|
+
### Fixed — wrong paths
|
|
103
|
+
|
|
104
|
+
- **A2A Agent Card moved.** The check probed `/.well-known/agent.json`. A2A relocated the card to `/.well-known/agent-card.json` in v0.3.0 (2025-07-30), and that path is IANA-registered. Both are probed, the registered one first; a card served only from the old path is validated and flagged. The check also handles both spec generations: A2A 1.0 (2026-03-12) folded `url`, `protocolVersion`, `preferredTransport` and `additionalInterfaces` into `supportedInterfaces[]`, and the generation is detected from the card's own structure rather than a version field. `authentication`, removed in 0.2.x in favour of `securitySchemes`, is now flagged. Renamed `agent-json` → `agent-card`.
|
|
105
|
+
- **`/.well-known/mcp.json` was never an MCP convention.** ax-audit recommended it before the ecosystem settled. Discovery now walks the real chain: `/.well-known/ai-catalog.json`, `/.well-known/mcp/server-card.json`, `<endpoint>/server-card`, then the legacy manifest. Server cards deliberately carry no `tools[]` — tool lists come from a live `tools/list` call. Protocol versions are checked against the five released revisions, with `2026-07-28` current. Renamed `mcp` → `mcp-discovery`.
|
|
106
|
+
- **`/.well-known/openapi.json` is a folk convention**, so a site publishing at `/openapi.json` scored zero. Discovery now runs in order of authority: RFC 9727's registered `/.well-known/api-catalog`, then RFC 8631 `service-desc` relations in headers and HTML, then ten conventional paths. YAML descriptions are recognised and reported as surface-validated only, since ax-audit ships no YAML parser. Renamed `openapi` → `api-discovery`.
|
|
107
|
+
- **`agent-access` was probing with `Google-Extended`.** That string is a robots.txt control token governing how an already-crawled page may be used; no request carries it, so the probe tested nothing. Token-only controls are excluded, leaving 10 real crawlers.
|
|
108
|
+
- **`http-headers` and `meta-tags` penalised correct behaviour**, requiring a discovery link to `agent.json` specifically. Either card path now counts.
|
|
109
|
+
- **An SPA catch-all page is absence, not corruption.** Found by running the new checks against a real site: probing `/mcp/server-card` returned HTTP 200 with the application's index shell, and the check reported a malformed server card on a site with no MCP server. `isHtmlDocument` now gates the speculative probes in `agent-card`, `mcp-discovery` and `api-discovery`.
|
|
110
|
+
|
|
111
|
+
### Fixed — crawler catalogue
|
|
112
|
+
|
|
113
|
+
Verified against vendor documentation on 2026-09-04. Removed tokens that were never real user agents (`Gemini`, `GeminiBot`, `DeepSeek-AI`), whose products were discontinued (`NeevaBot`, `Operator`, `GoogleAgent-Mariner`), whose vendor operates no crawlers (Cohere), or that are not AI crawlers at all (the Awario social-listening family). Reclassified five user-triggered fetchers that were listed as training crawlers. Added `meta-webindexer`, `Amzn-SearchBot`, `Amzn-User`, `MistralAI-Index`, `MistralAI-Training`, `Google-GeminiNotebook`, `Applebot`, `ExaSearchBot`, `TikTokSpider`, `OAI-AdsBot`, `YandexAdditional`.
|
|
114
|
+
|
|
115
|
+
The catalogue is now organised by what a client does with a page — training, search, user-fetch, agent — because that determines the cost of blocking it. `CRAWLER_META` carries per token the vendor, whether robots.txt is honored, the published IP list, whether requests are signed with Web Bot Auth, and one sentence on what blocking costs. Findings quote it: "blocking OAI-SearchBot removes you from ChatGPT search answers", not "1 AI crawler blocked".
|
|
116
|
+
|
|
117
|
+
### Added
|
|
118
|
+
|
|
119
|
+
- **Response classification.** `checks/waf.ts` distinguishes a deliberate block from a JavaScript challenge from anti-spoofing, because the remedies are completely different. Signatures verified 2026-09-04: `cf-mitigated: challenge`, `x-vercel-mitigated`, AWS WAF's 202 with `x-amzn-waf-action`, Cloudflare pay-per-crawl's `crawler-price`, x402's `payment-signature`, Web Bot Auth's `Accept-Signature`, RSL's `WWW-Authenticate: License`, plus thirteen body markers. Outcomes an unsigned probe cannot settle are scored at 0.75 and labelled inconclusive with the header observed — never as "blocks AI crawlers".
|
|
120
|
+
- **IETF AIPREF `Content-Usage:`** in robots.txt (draft-ietf-aipref-attach-05), with an optional path scope and a Structured Fields dictionary. The loudest finding is a vocabulary mix-up: AIPREF spells the training token `train-ai` with `y`/`n` while Content Signals and RSL spell it `ai-train` with `yes`/`no`, so `Content-Usage: ai-train=no` looks correct and does nothing.
|
|
121
|
+
- **Content Signals `use=immediate|reference|full`**, the fourth field Cloudflare added on 2026-07-01 and now serves from managed robots.txt. Cloudflare-managed blocks are detected and named.
|
|
122
|
+
- **Discovery link relations** in `http-headers`: `describedby` (llms.txt v2), `api-catalog` (RFC 9727), `service-desc` and `service-doc` (RFC 8631), `ai-catalog`, `c2pa-manifest`, `license`, markdown alternates, and the `X-Llms-Txt` header.
|
|
123
|
+
- **Content parity** in `agent-access` now fingerprints title, h1 and JSON-LD block count, catching cloaking that preserves word count.
|
|
124
|
+
- **Current well-known files** reported but never scored: the Web Bot Auth key directory, TDMRep, the OpenAI Apps verification token, `AGENTS.md`.
|
|
125
|
+
- **Fetcher transport options**: `method: 'HEAD'` and `redirect: 'manual'`, with `elapsedMs`, `redirected` and `redirectLocation` on every response.
|
|
126
|
+
- **Check categories** (content / discovery / access / policy / protocols) and `CheckResult.applicable` for N/A reporting, both consumed by reporters in 3.8.
|
|
127
|
+
- **`docs/roadmap.md`**: the research behind this release and the plan through 4.0.
|
|
128
|
+
|
|
129
|
+
### Changed
|
|
130
|
+
|
|
131
|
+
- **robots.txt is parsed once.** `checks/robots-parser.ts` replaces three separate parsers that disagreed about what closes a User-agent group. Two parsing bugs fell out: `Disallow:` with an empty value now correctly means "allow all" per RFC 9309 §2.2.2 instead of being ignored, and a bot named in two groups is merged so a later `Disallow: /` is no longer hidden by an earlier `Allow: /`.
|
|
132
|
+
- **`well-known-ai` tells the truth.** Re-verification found three of its five scored files have no consumer: `/.well-known/nlweb.json` **does not exist** in any NLWeb release, document or commit (NLWeb exposes `/ask` and `/mcp`); `genai.txt` has no specification; `/ai-plugin.json` described ChatGPT plugins, shut down 2024-04-09. Omitting a retired format now reads as a pass with the evidence attached. The formula stays frozen for score stability; the check loses its weight and its retired probes in 4.0.
|
|
133
|
+
- **Renamed check ids keep working.** `CheckMeta.aliases` plus `src/check-ids.ts` resolve former ids in `--checks` selection and in baseline diffing, so a rename does not read as one check removed at a full regression and one added at zero.
|
|
134
|
+
|
|
135
|
+
### Scoring
|
|
136
|
+
|
|
137
|
+
Unchanged. `robots-txt` deducts against the frozen eight-token core set and the token list 3.6 recognised; a site whose only MCP document is the legacy manifest is validated with the pre-3.7 rules reproduced exactly; `well-known-ai` keeps its five-file formula; every finding added inside a weighted check is informational. Broadened discovery can only raise a score, never lower one. Both freezes are removed in 4.0.
|
|
138
|
+
|
|
139
|
+
### Tests
|
|
140
|
+
|
|
141
|
+
553 total, up from 301. New suites: robots parser, structured fields, crawler catalogue, WAF classification, check-id aliasing, well-known registry, SPA-shell guard, fetcher transport. Two classes of test exist specifically to hold the no-regression promise: score-stability tests asserting a 3.6 perfect configuration still scores 100, and informational-finding tests asserting new findings leave scores untouched.
|
|
142
|
+
|
|
143
|
+
## [3.6.0] - 2026-06-06
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
|
|
147
|
+
- **Fetcher retries with exponential backoff**: transient failures (network errors, timeouts, and 408/425/429/500/502/503/504) are retried automatically. Configurable via `--retries <n>` (CLI, default 2) and `retries` (programmatic `AuditOptions`); backoff doubles from a 250ms base. Non-retryable responses (e.g. 404) short-circuit immediately. Previously a single transient timeout scored a check 0.
|
|
148
|
+
- **Parallel batch auditing**: `--concurrency <n>` (CLI) and `concurrency` on the new `BatchOptions` type run multiple URL audits in parallel via an order-preserving work queue. Default remains sequential (1).
|
|
149
|
+
- **Markdown reporter**: `--output markdown` emits a self-contained Markdown report (score, summary table, per-check findings with status emoji, baseline deltas) — ideal for CI logs and PR comments. Supported for single and batch audits. New exports: `renderMarkdown`, `renderBatchMarkdown`.
|
|
150
|
+
- **Crawler list refresh**: added Google's official signed AI-agent user-agent `Google-Agent` (identity `https://agent.bot.goog`) to the known-crawlers list.
|
|
151
|
+
- **CLI validation**: `--retries`, `--concurrency`, and `--output` now reject invalid values with a clear error.
|
|
152
|
+
- **17 new tests** (301 total): fetcher retry behavior (against a flaky local server), batch ordering/concurrency, and the Markdown reporter.
|
|
153
|
+
|
|
154
|
+
### Notes
|
|
155
|
+
|
|
156
|
+
- No scoring changes. Retries can raise scores on flaky endpoints that previously timed out, but the scoring model itself is unchanged.
|
|
157
|
+
|
|
158
|
+
## [3.5.0] - 2026-06-06
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
|
|
162
|
+
- **crawl-efficiency check (informational)**: measures the cost of crawling your pages across three dimensions. Compression — rewards Brotli, accepts gzip/deflate/zstd (suggesting br), warns when uncompressed (−30). Conditional GET — checks for an `ETag` or `Last-Modified` validator, then issues a follow-up request with `If-None-Match` / `If-Modified-Since` and verifies the server returns `304 Not Modified` (−30 for no validator, −15 when 304 is not honored). Response size — warns on pages over 500 KB (−5) and 2 MB (−10) of decompressed HTML. The probe advertises `Accept-Encoding: br, gzip, deflate`; the conditional request reuses the per-request header support added in 3.1.0.
|
|
163
|
+
- **12 new tests** (284 total).
|
|
164
|
+
|
|
165
|
+
### Scoring
|
|
166
|
+
|
|
167
|
+
- The new check carries **weight 0 in 3.x** (informational), consistent with 3.1.0–3.4.0.
|
|
168
|
+
|
|
169
|
+
## [3.4.0] - 2026-06-06
|
|
170
|
+
|
|
171
|
+
### Added
|
|
172
|
+
|
|
173
|
+
- **agent-access check (informational)**: cloaking and blocking detection. Probes the homepage with realistic user-agents for each of the 8 core AI crawlers (GPTBot, ClaudeBot, ChatGPT-User, Claude-SearchBot, Google-Extended, PerplexityBot, OAI-SearchBot, CCBot) and compares status and visible-text volume against the default-UA baseline. Flags the failure mode invisible to operators: robots.txt allows a crawler while the WAF returns 403 to its user-agent (Cloudflare's "Block AI Crawlers" toggle produces exactly this). Blocks consistent with an explicit robots.txt `Disallow` (or wildcard block) are reported as intentional and not penalized. Responses with under 50% of baseline visible text count as reduced content (half credit); content comparison is skipped for baselines under 200 chars to avoid SPA-shell noise. Hints note the verified-bots caveat: WAFs using Web Bot Auth / IP verification may pass the real crawler while rejecting this unverified probe.
|
|
174
|
+
- `parseUserAgents` and `BotEntry` are now exported from the robots-txt check for reuse.
|
|
175
|
+
- **12 new tests** (272 total).
|
|
176
|
+
|
|
177
|
+
### Scoring
|
|
178
|
+
|
|
179
|
+
- Internal score is the credit ratio across the 8 probes; the check carries **weight 0 in 3.x** (informational), consistent with 3.1.0–3.3.0.
|
|
180
|
+
|
|
181
|
+
## [3.3.0] - 2026-06-06
|
|
182
|
+
|
|
183
|
+
### Added
|
|
184
|
+
|
|
185
|
+
- **rsl check (informational)**: validates [Really Simple Licensing 1.0](https://rslstandard.org/rsl) — the machine-readable content-licensing standard endorsed by 1,500+ publishers (Reddit, Yahoo, Medium, O'Reilly) with infrastructure support from Cloudflare and Fastly. Discovery via all three spec mechanisms: robots.txt `License:` directive (absolute-URI enforcement per §4.4.1), HTTP `Link: rel="license"; type="application/rsl+xml"` header, and `<link rel="license" type="application/rsl+xml">` (plain CC-style license links without the RSL media type are ignored). Document validation: `application/rsl+xml` Content-Type (−5), `<rsl>` root + `https://rslstandard.org/rsl` namespace, required `url` attribute on every `<content>` (empty value allowed per §3.3), `<license>` presence, `permits`/`prohibits` type and token vocabulary (`usage`: all/ai-all/ai-train/ai-input/ai-index/search; `user`; `geo` as ISO 3166-1 alpha-2), and `payment` types.
|
|
186
|
+
- **21 new tests** (260 total) covering the three discovery mechanisms, vocabulary enforcement, namespace/root/structure validation, XML-comment stripping, and score caps.
|
|
187
|
+
|
|
188
|
+
### Scoring
|
|
189
|
+
|
|
190
|
+
- The new check carries **weight 0 in 3.x** (informational), consistent with 3.1.0/3.2.0: no impact on existing scores or baselines until v4.0.
|
|
191
|
+
|
|
192
|
+
## [3.2.0] - 2026-06-06
|
|
193
|
+
|
|
194
|
+
### Added
|
|
195
|
+
|
|
196
|
+
- **Content Signals Policy support in robots-txt** ([contentsignals.org](https://contentsignals.org), CC0): the check now parses `Content-Signal:` directives — the machine-readable `search` / `ai-input` / `ai-train` preferences that Cloudflare serves by default on its 3.8M+ managed robots.txt domains. Declared signals are reported per User-agent group; malformed segments, unknown signal names, and directives placed outside a User-agent group produce warnings. Absence of the directive produces an informational nudge. The group parser now also treats `Content-Signal` as a group-closing directive, fixing potential User-agent group leakage.
|
|
197
|
+
- **10 new tests** (239 total) covering declaration reporting, malformed/unknown signals, shared User-agent groups, case-insensitivity, out-of-group placement, and score neutrality.
|
|
198
|
+
|
|
199
|
+
### Scoring
|
|
200
|
+
|
|
201
|
+
- All Content Signals findings are **informational in 3.x**: they never alter the robots-txt score, so existing scores and baselines are unchanged.
|
|
202
|
+
|
|
5
203
|
## [3.1.0] - 2026-06-06
|
|
6
204
|
|
|
7
205
|
### Added
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">ax-audit</h1>
|
|
6
6
|
|
|
7
|
-
[](https://github.com/duranitech/ax-audit/actions/workflows/ci.yml)
|
|
8
8
|
[](https://www.npmjs.com/package/ax-audit)
|
|
9
|
-
[](https://github.com/
|
|
9
|
+
[](https://github.com/duranitech/ax-audit/blob/main/LICENSE)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
|
|
12
12
|
**Lighthouse for AI Agents.** Audit any website's AI Agent Experience (AX) readiness in seconds.
|
|
@@ -17,281 +17,139 @@ npx ax-audit https://your-site.com
|
|
|
17
17
|
|
|
18
18
|
```
|
|
19
19
|
AX Audit Report
|
|
20
|
-
https://
|
|
21
|
-
|
|
22
|
-
███████████████████████████████████░░░░░ 88/100 Good
|
|
23
|
-
|
|
24
|
-
LLMs.txt (100/100)
|
|
25
|
-
PASS /llms.txt exists
|
|
26
|
-
PASS /llms.txt Content-Type OK (text/plain)
|
|
27
|
-
PASS H1 heading: "Lucio Duran — Personal Portfolio"
|
|
28
|
-
PASS /llms-full.txt also available (bonus)
|
|
29
|
-
|
|
30
|
-
Robots.txt (100/100)
|
|
31
|
-
PASS All 8 core AI crawlers explicitly configured
|
|
32
|
-
PASS 32/47 known AI crawlers have explicit rules
|
|
33
|
-
|
|
34
|
-
HTML Rendering (90/100)
|
|
35
|
-
PASS Server-rendered content detected (473 words)
|
|
36
|
-
PASS Semantic landmarks present (main, article, header, footer, nav)
|
|
37
|
-
PASS Single <h1> heading
|
|
38
|
-
PASS 3/3 <img> tags have alt attributes
|
|
39
|
-
|
|
40
|
-
TLS / HTTPS (100/100)
|
|
41
|
-
PASS Site is served over HTTPS
|
|
42
|
-
PASS HTTP requests redirect to HTTPS
|
|
43
|
-
PASS HSTS preload-eligible
|
|
44
|
-
...
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Why
|
|
48
|
-
|
|
49
|
-
AI agents and LLMs are increasingly crawling, indexing, and interacting with websites. Just like Lighthouse audits web performance and axe-core audits accessibility, **ax-audit** tells you how ready your site is for the AI agent ecosystem.
|
|
50
|
-
|
|
51
|
-
## What it checks
|
|
52
|
-
|
|
53
|
-
| Check | What it audits | Weight |
|
|
54
|
-
|---|---|---|
|
|
55
|
-
| **LLMs.txt** | `/llms.txt` presence, [llmstxt.org](https://llmstxt.org) spec, Content-Type | 11% |
|
|
56
|
-
| **Robots.txt** | AI crawler configuration (40+ known crawlers), wildcard detection, partial path restrictions | 11% |
|
|
57
|
-
| **HTML Rendering** | Server-rendered content, semantic landmarks, SPA-shell detection, alt coverage | 9% |
|
|
58
|
-
| **Structured Data** | JSON-LD on homepage (schema.org, `@graph`, entity types) | 9% |
|
|
59
|
-
| **HTTP Headers** | Security headers + AI discovery `Link` headers + CORS on `.well-known` | 9% |
|
|
60
|
-
| **Agent Card** | `/.well-known/agent.json` [A2A protocol](https://a2a-protocol.org) + same-origin url + skill quality | 7% |
|
|
61
|
-
| **MCP** | `/.well-known/mcp.json` [Model Context Protocol](https://modelcontextprotocol.io) server config | 7% |
|
|
62
|
-
| **SEO Basics** | `<title>`, meta description, canonical, `<html lang>`, charset, viewport, hreflang | 7% |
|
|
63
|
-
| **Security.txt** | `/.well-known/security.txt` [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116) compliance | 6% |
|
|
64
|
-
| **Meta Tags** | AI meta tags (`ai:*`), `rel="alternate"`, `rel="me"`, OpenGraph + Twitter Card completeness | 6% |
|
|
65
|
-
| **OpenAPI** | `/.well-known/openapi.json` presence, schema validity, Content-Type | 6% |
|
|
66
|
-
| **TLS / HTTPS** | HTTPS, HTTP→HTTPS redirect, HSTS with `preload` + `includeSubDomains` | 5% |
|
|
67
|
-
| **Sitemap** | `sitemap.xml` (or `Sitemap:` from robots.txt) — XML validity, `<lastmod>` coverage, freshness, sitemap-index handling | 4% |
|
|
68
|
-
| **AI Well-Known** | Emerging files: `/.well-known/ai.txt`, `genai.txt`, `ai-plugin.json`, `agents.json`, `nlweb.json` | 3% |
|
|
69
|
-
| **Content Negotiation** | Markdown for agents — `Accept: text/markdown` negotiation, `Vary: Accept`, `rel="alternate"` fallback | 0%* |
|
|
70
|
-
|
|
71
|
-
\* **Content Negotiation** is informational in 3.x: it runs and reports findings but does not affect the overall score. It will gain weight in v4.0.
|
|
72
|
-
|
|
73
|
-
## Install
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
npm install -g ax-audit
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Or run directly without installing:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
npx ax-audit https://your-site.com
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Usage
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
# Full audit with colored terminal output
|
|
89
|
-
ax-audit https://example.com
|
|
90
|
-
|
|
91
|
-
# Batch audit — audit multiple URLs in a single run
|
|
92
|
-
ax-audit https://example.com https://other-site.com https://third.dev
|
|
93
|
-
|
|
94
|
-
# HTML report — self-contained, dark mode, shareable
|
|
95
|
-
ax-audit https://example.com --output html > report.html
|
|
96
|
-
|
|
97
|
-
# JSON output for CI/CD pipelines
|
|
98
|
-
ax-audit https://example.com --json
|
|
99
|
-
|
|
100
|
-
# Run only specific checks (validates IDs, errors on unknown)
|
|
101
|
-
ax-audit https://example.com --checks llms-txt,robots-txt,agent-json
|
|
102
|
-
|
|
103
|
-
# Custom timeout per request (default: 10s)
|
|
104
|
-
ax-audit https://example.com --timeout 15000
|
|
105
|
-
|
|
106
|
-
# Verbose mode — see every HTTP request, cache hit, and check score
|
|
107
|
-
ax-audit https://example.com --verbose
|
|
108
|
-
|
|
109
|
-
# Only show failures and warnings (hide passing findings)
|
|
110
|
-
ax-audit https://example.com --only-failures
|
|
111
|
-
|
|
112
|
-
# Save a baseline for future comparison
|
|
113
|
-
ax-audit https://example.com --save-baseline baseline.json
|
|
114
|
-
|
|
115
|
-
# Compare against a baseline — shows per-check score deltas
|
|
116
|
-
ax-audit https://example.com --baseline baseline.json
|
|
20
|
+
https://example.com
|
|
117
21
|
|
|
118
|
-
|
|
119
|
-
ax-audit https://example.com --baseline baseline.json --fail-on-regression 5
|
|
120
|
-
```
|
|
22
|
+
█████████████████████████████████░░░░░░░ 83/100 Good
|
|
121
23
|
|
|
122
|
-
|
|
24
|
+
── Content — is there substance an agent can read?
|
|
123
25
|
|
|
124
|
-
|
|
26
|
+
Structured Data (90/100)
|
|
27
|
+
PASS 3 JSON-LD block(s) found
|
|
28
|
+
WARN 1 structured-data value(s) do not appear in the visible text
|
|
29
|
+
💡 Google's one explicit requirement for structured data and AI
|
|
30
|
+
features is that it match what a reader sees.
|
|
125
31
|
|
|
126
|
-
|
|
127
|
-
# First run — save the baseline
|
|
128
|
-
ax-audit https://example.com --save-baseline .ax-baseline.json
|
|
32
|
+
── Access — can an agent actually retrieve it?
|
|
129
33
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
```
|
|
34
|
+
Agent Access (100/100)
|
|
35
|
+
PASS All 10 core AI crawler user-agents receive the same page as a regular client
|
|
133
36
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Baseline: 2026-04-15T12:00:00.000Z
|
|
37
|
+
AI Directives (100/100)
|
|
38
|
+
PASS Homepage is indexable
|
|
39
|
+
PASS No directive restricts how AI assistants may use this page
|
|
138
40
|
|
|
139
|
-
|
|
41
|
+
── Protocols — what can an agent call?
|
|
140
42
|
|
|
141
|
-
|
|
142
|
-
|
|
43
|
+
MCP Discovery (n/a)
|
|
44
|
+
PASS No MCP server — MCP discovery does not apply to this site
|
|
143
45
|
...
|
|
144
|
-
|
|
145
|
-
Regressions
|
|
146
|
-
Robots.txt: 80 → 70 (▼10)
|
|
147
|
-
|
|
148
|
-
Improvements
|
|
149
|
-
LLMs.txt: 80 → 100 (▲20)
|
|
150
46
|
```
|
|
151
47
|
|
|
152
|
-
|
|
48
|
+
## Why
|
|
153
49
|
|
|
154
|
-
|
|
50
|
+
AI agents crawl, cite and act on websites. Lighthouse audits performance, axe-core audits accessibility, and **ax-audit** tells you how ready your site is for agents — discovery files, crawler policy, usage rights, content negotiation, and the failures that are invisible from the inside:
|
|
155
51
|
|
|
156
|
-
|
|
52
|
+
- Your robots.txt allows GPTBot and your firewall returns 403 to it.
|
|
53
|
+
- You blocked `Google-Extended` expecting to leave AI Overviews. It does not do that.
|
|
54
|
+
- Your robots.txt permits AI training while your RSL licence prohibits it, so which terms apply depends on which file a crawler read.
|
|
55
|
+
- Your content only exists after hydration, so the crawlers that do not run JavaScript see an empty page.
|
|
56
|
+
- A missing page answers `200 OK`, so an agent stores the apology as the answer.
|
|
157
57
|
|
|
158
|
-
|
|
58
|
+
## What it checks
|
|
159
59
|
|
|
160
|
-
|
|
161
|
-
═══ Batch Summary ═══
|
|
60
|
+
26 checks across five areas. Full reference: **[docs/checks.md](docs/checks.md)**.
|
|
162
61
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
62
|
+
| Area | Weight | Checks |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| **Content** — is there substance an agent can read? | 33% | HTML Rendering · Agent Operability · Structured Data · SEO Basics · Content Negotiation |
|
|
65
|
+
| **Access** — can an agent actually retrieve it? | 24% | Agent Access · AI Directives · HTTP Hygiene · TLS/HTTPS · Crawl Efficiency |
|
|
66
|
+
| **Discovery** — can an agent find your machine-readable files? | 21% | Robots.txt · LLMs.txt · HTTP Headers · Sitemap · Meta Tags |
|
|
67
|
+
| **Protocols** — what can an agent call? | 13% | API Discovery · Agent Card · MCP Discovery · Agent Skills · Auth Discovery |
|
|
68
|
+
| **Policy** — what usage rights do you declare? | 9% | Usage Policy · Security.txt · RSL License |
|
|
69
|
+
| Draft specifications, reported but never scored | 0% | AI Catalog · WebMCP · Commerce Discovery |
|
|
167
70
|
|
|
168
|
-
|
|
169
|
-
████████████████████████████░░░░░░░░░░░░ 69/100 avg Fair
|
|
170
|
-
```
|
|
71
|
+
Content leads because the failure that breaks the most agents is a page with nothing in its HTML. Most crawlers do not run JavaScript, so a site whose content appears only after hydration is invisible to them no matter how many discovery files it publishes.
|
|
171
72
|
|
|
172
|
-
|
|
73
|
+
Protocol checks are **conditional**: a blog has no API to describe, so those report `n/a` and are excluded from the score rather than counted as failures. Use `--profile` to audit against what a site intends to build.
|
|
173
74
|
|
|
174
|
-
|
|
75
|
+
Every finding links to a step-by-step **[remediation guide](https://axrush.com/guides)**.
|
|
175
76
|
|
|
176
|
-
|
|
77
|
+
## Usage
|
|
177
78
|
|
|
178
79
|
```bash
|
|
179
|
-
ax-audit https://example.com
|
|
80
|
+
ax-audit https://example.com # full audit, terminal output
|
|
81
|
+
ax-audit https://a.com https://b.com --concurrency 2 # batch, in parallel
|
|
82
|
+
ax-audit https://example.com --output markdown # also: json, html
|
|
83
|
+
ax-audit https://example.com --checks llms-txt,rsl # subset of checks
|
|
84
|
+
ax-audit https://example.com --only-failures # hide passing findings
|
|
85
|
+
ax-audit https://example.com --category access # one area only
|
|
86
|
+
ax-audit https://example.com --profile api # audit as though it had an API
|
|
87
|
+
ax-audit https://example.com --fail-on-category access:70 # per-area CI gate
|
|
88
|
+
ax-audit https://example.com --baseline .ax-baseline.json --fail-on-regression 5
|
|
180
89
|
```
|
|
181
90
|
|
|
182
|
-
|
|
91
|
+
Exit codes gate CI: `0` for score ≥ 70, `1` below. Full flag reference: **[docs/cli.md](docs/cli.md)** · CI recipes (PR comments, regression gates, scheduled audits): **[docs/ci.md](docs/ci.md)**.
|
|
183
92
|
|
|
184
93
|
## Programmatic API
|
|
185
94
|
|
|
186
|
-
Full TypeScript support with all types exported.
|
|
187
|
-
|
|
188
95
|
```typescript
|
|
189
96
|
import { audit, batchAudit } from 'ax-audit';
|
|
190
|
-
import type { AuditReport, BatchAuditReport } from 'ax-audit';
|
|
191
|
-
|
|
192
|
-
// Single URL
|
|
193
|
-
const report: AuditReport = await audit({ url: 'https://example.com' });
|
|
194
|
-
console.log(report.overallScore); // 0-100
|
|
195
|
-
console.log(report.grade.label); // 'Excellent' | 'Good' | 'Fair' | 'Poor'
|
|
196
|
-
console.log(report.results); // Individual check results with findings
|
|
197
|
-
|
|
198
|
-
// Batch audit
|
|
199
|
-
const batch: BatchAuditReport = await batchAudit(
|
|
200
|
-
['https://example.com', 'https://other.com'],
|
|
201
|
-
{ timeout: 10000 }
|
|
202
|
-
);
|
|
203
|
-
console.log(batch.summary.averageScore); // Average across all URLs
|
|
204
|
-
console.log(batch.summary.passed); // Number of URLs scoring >= 70
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Also exports `calculateOverallScore`, `getGrade`, `checks`, `saveBaseline`, `loadBaseline`, `diffBaseline`, and `toBaselineData` for advanced usage.
|
|
208
|
-
|
|
209
|
-
## Scoring
|
|
210
|
-
|
|
211
|
-
Each check returns a score from 0 to 100. The overall score is a weighted average across all checks.
|
|
212
97
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
| Fair | 50 - 69 | `1` |
|
|
218
|
-
| Poor | 0 - 49 | `1` |
|
|
219
|
-
|
|
220
|
-
Exit codes make it easy to gate CI/CD deployments on AX readiness.
|
|
98
|
+
const report = await audit({ url: 'https://example.com' });
|
|
99
|
+
report.overallScore; // 0–100
|
|
100
|
+
report.results; // per-check findings
|
|
101
|
+
```
|
|
221
102
|
|
|
222
|
-
|
|
103
|
+
Full API and types: **[docs/api.md](docs/api.md)**.
|
|
223
104
|
|
|
224
|
-
|
|
105
|
+
## Documentation
|
|
225
106
|
|
|
226
|
-
|
|
227
|
-
- name: AX Audit
|
|
228
|
-
run: npx ax-audit https://your-site.com
|
|
229
|
-
# Fails the step if score < 70
|
|
230
|
-
```
|
|
107
|
+
Start here:
|
|
231
108
|
|
|
232
|
-
|
|
109
|
+
| Document | Contents |
|
|
110
|
+
|---|---|
|
|
111
|
+
| [docs/getting-started.md](docs/getting-started.md) | First audit, reading the report, fixing in impact order |
|
|
112
|
+
| [docs/concepts.md](docs/concepts.md) | The AX standards landscape — llms.txt, A2A, MCP, RSL, Content Signals, Web Bot Auth |
|
|
233
113
|
|
|
234
|
-
|
|
235
|
-
- name: AX Audit (JSON)
|
|
236
|
-
run: npx ax-audit https://your-site.com --json > ax-report.json
|
|
114
|
+
Reference:
|
|
237
115
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
116
|
+
| Document | Contents |
|
|
117
|
+
|---|---|
|
|
118
|
+
| [docs/checks.md](docs/checks.md) | All 26 checks with **exact scoring** per finding, the weight table, and the conditional-check rule |
|
|
119
|
+
| [docs/cli.md](docs/cli.md) | Every flag, profiles, area filters, per-area CI gates, baseline workflow |
|
|
120
|
+
| [docs/api.md](docs/api.md) | `audit`, `batchAudit`, baselines, reporters, types, API-stability policy |
|
|
121
|
+
| [docs/ci.md](docs/ci.md) | GitHub Actions recipes: gates, PR comments, scheduled drift detection |
|
|
122
|
+
| [docs/architecture.md](docs/architecture.md) | Pipeline design, check anatomy, how to add a check, scoring policy |
|
|
123
|
+
| [docs/faq.md](docs/faq.md) | Troubleshooting, false positives, the `agent-access` verified-bots caveat |
|
|
124
|
+
| [Remediation guides](https://axrush.com/guides) | Step-by-step fixes for every finding |
|
|
243
125
|
|
|
244
|
-
|
|
126
|
+
The same documentation is browsable at [axrush.com/docs](https://axrush.com/docs), rendered from these files. Contributors: see [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
|
|
245
127
|
|
|
246
|
-
|
|
247
|
-
- name: AX Audit (regression gate)
|
|
248
|
-
run: npx ax-audit https://your-site.com --baseline .ax-baseline.json --fail-on-regression 5
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
## Available Checks
|
|
128
|
+
## Scoring
|
|
252
129
|
|
|
253
|
-
|
|
|
254
|
-
|
|
255
|
-
|
|
|
256
|
-
|
|
|
257
|
-
|
|
|
258
|
-
|
|
|
259
|
-
| `http-headers` | Security + AI discovery headers |
|
|
260
|
-
| `agent-json` | A2A Agent Card + same-origin validation |
|
|
261
|
-
| `mcp` | MCP server configuration |
|
|
262
|
-
| `seo-basics` | title / description / canonical / lang / hreflang |
|
|
263
|
-
| `security-txt` | RFC 9116 Security.txt |
|
|
264
|
-
| `meta-tags` | AI meta tags + OpenGraph + Twitter Card |
|
|
265
|
-
| `openapi` | OpenAPI specification |
|
|
266
|
-
| `tls-https` | HTTPS + HTTP→HTTPS redirect + HSTS preload |
|
|
267
|
-
| `sitemap` | sitemap.xml validation + freshness |
|
|
268
|
-
| `well-known-ai` | Emerging AI discovery files |
|
|
269
|
-
| `content-negotiation` | Markdown via `Accept: text/markdown` (informational) |
|
|
270
|
-
|
|
271
|
-
## Testing
|
|
130
|
+
| Grade | Score | Exit Code |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| Excellent | 90–100 | `0` |
|
|
133
|
+
| Good | 70–89 | `0` |
|
|
134
|
+
| Fair | 50–69 | `1` |
|
|
135
|
+
| Poor | 0–49 | `1` |
|
|
272
136
|
|
|
273
|
-
|
|
274
|
-
npm test
|
|
275
|
-
```
|
|
137
|
+
Checks that do not apply to a site report **n/a** and leave the denominator entirely, rather than scoring 0. A blog is not marked down for having no API to describe. Everything counted against a site is something the site could have done — which is what makes a low score worth acting on.
|
|
276
138
|
|
|
277
|
-
|
|
139
|
+
`--fail-on-category access:70` gates CI per area, because an overall score can hide an area that is entirely broken while the other four carry it.
|
|
278
140
|
|
|
279
|
-
## Tech
|
|
141
|
+
## Tech
|
|
280
142
|
|
|
281
|
-
-
|
|
282
|
-
- **2 runtime dependencies**: `chalk` + `commander`
|
|
283
|
-
- **Node.js 18+** built-in `fetch` (zero HTTP libraries)
|
|
284
|
-
- Parallel check execution via `Promise.allSettled`
|
|
285
|
-
- In-memory request caching per audit run
|
|
143
|
+
TypeScript strict mode · 2 runtime dependencies (`chalk`, `commander`) · Node 18+ built-in `fetch` · no HTML, XML or YAML parser dependencies · parallel checks via `Promise.allSettled` · per-run request cache with `Vary`-aware keys · transient-failure retries with backoff · 873 tests on `node:test` with zero test dependencies.
|
|
286
144
|
|
|
287
145
|
## Contributing
|
|
288
146
|
|
|
289
|
-
Contributions are welcome.
|
|
147
|
+
Contributions are welcome — see **[docs/architecture.md](docs/architecture.md)** for the pipeline design, check anatomy, and the steps (code, tests, docs, remediation guide) a new check requires.
|
|
148
|
+
|
|
149
|
+
## Related
|
|
290
150
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
3. Register it in `src/checks/index.ts`
|
|
294
|
-
4. Add its weight to `CHECK_WEIGHTS` in `src/constants.ts`
|
|
151
|
+
- **[ax-init](https://github.com/duranitech/ax-init)** — generate the AX files this tool audits
|
|
152
|
+
- **[ax-cite](https://github.com/duranitech/ax-cite)** — embed AI-extractable structured data in your pages
|
|
295
153
|
|
|
296
154
|
## License
|
|
297
155
|
|
|
@@ -299,4 +157,4 @@ Contributions are welcome. To add a new check:
|
|
|
299
157
|
|
|
300
158
|
---
|
|
301
159
|
|
|
302
|
-
|
|
160
|
+
**[AX Rush](https://axrush.com)** — the agent-experience toolkit, by [Durani Technologies](https://github.com/duranitech).
|
package/dist/baseline.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { AuditReport, BaselineData, BaselineDiff } from './types.js';
|
|
|
3
3
|
* Extract a minimal, stable snapshot from an AuditReport suitable for
|
|
4
4
|
* persistence and future comparison.
|
|
5
5
|
*/
|
|
6
|
+
/** Scoring model this build produces. Bumped whenever weights or the scoring rules change. */
|
|
7
|
+
export declare const BASELINE_SCHEMA_VERSION = 2;
|
|
6
8
|
export declare function toBaselineData(report: AuditReport): BaselineData;
|
|
7
9
|
/**
|
|
8
10
|
* Persist a baseline to disk as pretty-printed JSON.
|
package/dist/baseline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAErF;;;GAGG;AACH,8FAA8F;AAC9F,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAiBhE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAIpE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAwBvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,YAAY,CAkEtF"}
|