v0.0.108
Released Oct 10, 2026
Release Notes
Audits now come in three sizes, called levels: quick, surface and full. A level is one name for a whole set of settings (pages, crawl strategy, cloud checks, rendering, external link checks and probing), defined once and shared by the CLI, the local MCP server and the dashboard, so a surface audit means the same thing wherever you start it. A signed-in cloud audit costs credits at every level, quick included: 50 for the audit plus 2 per audited page. Signed out or with --offline, the audit runs on your machine and costs nothing. The Audit levels guide has what each level reads, checks and costs. Also in this release: the local MCP audit_website tool now bills a signed-in audit the way squirrel audit does, a dev server on localhost stops getting marked down for having no HTTPS, squirrel self uninstall is safe for package managers to call, and a pile of rule fixes.
Added
- Audit levels:
--level quick|surface|full. A level is a named set of audit settings (pages, crawl strategy, cloud checks, rendering, external link checks and probing), defined once and read bysquirrel audit,squirrel crawl, the local MCP tools, the config and the shell completions. Quick reads up to 25 pages from the URL and its sitemaps (it follows links only when the sitemaps give it nothing to crawl), surface up to 100 pages with one page per URL pattern first, and full every page it can reach, up to 500. Without--levelor[crawler] coverage,squirrel auditruns surface when you are signed in and quick when you are signed out or your session has expired, andsquirrel crawlruns quick.--coverage/-Cand[crawler] coveragekeep working as the older names andfaststill means quick. An unknown level, a repeated--levelor-C, or two flags naming different levels stop with an error before the crawl starts. Picking a level fills in every setting; the flags and config values you already use (--max-pages,--render-mode,--http,--probe,[external_links] enabled = falseand the rest) change single settings, and an audit whose settings differ from its level becomes custom. The run banner says which:Level surface · max 100 pages, orLevel custom (surface + max_pages 200). See Audit levels. - Reports say which level they ran at. The JSON report has
meta.auditLevel(level,basedOn,changes,settings), the LLM report an<audit-level>element, and the text, Markdown and HTML reports anAudit level:line. A JSON report loaded back withsquirrel reportkeeps it. No existing field changes. - The local MCP
audit_websitetool takeslevel(coveragestill works) and defaults to surface. It applies the project config's page limit, rendering, external link and probing settings the waysquirrel auditdoes, including[crawler] max_pages. - After the crawl, the audit lists the pages whose response could not be decoded, with each URL and its declared content-encoding: the first five, then a count of the rest.
Changed
- The local MCP
audit_websitetool bills a signed-in audit the waysquirrel auditdoes, at every level: it registers the run (50 credits) and settles it on its audited pages (2 each), renders pages in the cloud browser as the level says (quick only the pages that need JavaScript, surface and full every page), and runs the cloud checks at surface and full. Before, it could run the cloud checks without registering an audit. A tool call cannot prompt, so when the estimate is above[cloud] confirm_threshold(50 by default, as in the CLI) the call returns it asconfirmation_required, and the agent calls again withconfirm: true. The page budget is fitted to the balance and to[cloud] max_credits_per_audit; an audit that cannot pay for even one page is refused with the cost and the balance, and so is one the server will not register. An audit that errors asks the server to mark its run failed, and stopping the server with Ctrl-C or a termination signal asks it to mark the run cancelled, so both settle like a failed or cancelledsquirrel audit. Signed out, withoffline: true, against a local or private host, and inquick_check, the tools still run on your machine with no cloud call. - A level's settings are now the defaults. Quick no longer checks external links and probes passively, while surface and full check external links and probe actively, signed in or not. Before, external links were checked at every level and probing followed sign-in instead of the level. A signed-out audit with no flags runs quick, so it now skips external link checks. Explicit flags and config values win as before.
max_pages = 100, whichsquirrel initwrites into every config, still reads as unset, and so does[external_links] enabled = true, which it also writes, so a generated config no longer turns external link checks on at quick. Set--level surfaceorfullto get them back. - Hints and locked-check messages point at
--levelinstead of-C, and the quick-level message no longer says "coverage". security/leaked-secretsreports a server-only secret found in a browser-served script (database connection strings, service-role andsk_live_keys, AWS secret keys and similar) in a newleaked-secrets-criticalcheck, "shipped to every visitor". Publishable keys, DigitalOcean Spaces key ids, tutorial values (AWS's documented example keys, placeholder orlocalhostdatabase URLs) and hits only in page HTML are unchanged.perf/bad-cachingexplains a missing ETag behind Cloudflare. Cloudflare drops theETagfrom HTML it rewrites (Email Address Obfuscation, Automatic HTTPS Rewrites, Replace insecure JavaScript libraries, and JavaScript Detections, which Bot Fight Mode turns on), sometimes only for requests that ask for a page, so a plaincurl -Ican show a header browsers never get. When the validators check warns and at least half of the pages missing a validator came through Cloudflare, the finding now says the CDN may be the cause and lists the fixes:no-transformin the HTMLCache-Control, turning those features off, or also sendingLast-Modified. The validators check now warns instead of failing, because a page with no lifetime and no validator already counts against the freshness check. A weakETag(W/"...") still counts as a validator for every content encoding. The rule page shows how to check which side removes it.crawl/pdf-sizedescribes its 60MB failure threshold as squirrel's default, not as Google's limit. Google documents 64MB for PDFs; the rule's messages, description and option text now say so.security/sri,content/meta-in-bodyandschema/rating-scopeuse one message wording whatever the count, andschema/rating-scopeno longer puts the rated entity's name in its check message, so one defect keeps one finding identity across pages.squirrel self uninstallonly deletes what a squirrelscan install created, and is ready for package managers to call. It removes thesquirrellink (in the default bin directory and the one recorded byself install --bin-dir) and~/.squirrel/releases, and keeps settings, credentials and audit data unless you pass--purge, which now also removes the cache directory. Each link is checked against the managed install before anything is removed: a link pointing anywhere else, an unrelated file at the link path, or a symlinked~/.squirrelis reported and left alone, and a symlink inside the releases is never followed. It will not delete a managed install when run from some othersquirrelbinary. It asks before deleting, and without a terminal it refuses unless you pass--yes(the new name for--force, which still works). With nothing left to remove, it exits 0. See self uninstall.- The install docs cover Homebrew:
brew install --cask squirrelscan/tap/squirrelinstalls a launcher that runs the regular installer on first use, after whichsquirrel self updateowns updates.brew uninstall --cask squirrelrunssquirrel self uninstall --yes, and--zapalso removes~/.squirrel. See the quickstart.
Fixed
- A clean site audited on
localhostor a private-network host no longer scores a C or D for being a dev server. The transport and delivery rules (security/https,security/hsts,security/http-to-https,perf/http2,perf/compression,perf/asset-compression,perf/cache-headers,perf/bad-caching) are skipped there and left out of the score, so the same cause (no HTTPS, no compression, no caching policy) is no longer charged by several rules at once. The report says so in one line under the score, and the JSON report asnotApplicable. The host is classified from the audit URL by the same check the cloud handoff already used, now shared in@squirrelscan/utils; public hosts score exactly as before. See Transport and delivery rules do not apply. - The docs said local audits were free. The agent setup pages, the cloud overview, the setup guides and every rule page now say it plainly: signed-out local audits need no account and cost nothing, and signed-in audits spend credits.
squirrel crawl -C fastcrawled with no page limit at all: the crawl cast the flag to a level without checking it, the page lookup forfastcame back empty and the cap becameNaN. The crawl now reads its level through the same parser asaudit, sofastis quick (25 pages) and an unknown level stops before the crawl starts. A non-numeric--max-pagesis refused too, instead of the same unbounded crawl.- The local MCP
quick_checktool described itself as a single-page check. It reads the URL and its sitemaps, 25 pages by default, and now says so. It also follows the project's[crawler] max_pagesand[security] probesettings. - The console report footer counts warnings the same way as its "Total:" line. Before, the footer counted advisory warnings from severity-"info" rules and the score line did not, so one report could show two warning totals.
squirrel audit -f json -o <file>prints the "JSON report saved to" line on stderr instead of stdout.- A malformed
squirrel.tomlprints the "TOML syntax error:" line with the parser detail again. The check compared the error name, which the parser never sets, so the labelled message never showed. content/heading-hierarchydoes not count an empty heading the markup hides (hidden, inlinedisplay:noneorvisibility:hidden, or a hidden oraria-hiddenancestor). The parser marks those headings, so a cached parse counts the same as a fresh one.- A signed-in audit of a localhost, private-network or other non-public host no longer sends the archive-indexing lookup to the cloud, where it could only come back empty.
- Template fan-out, which lets the pages of one template share a rule's verdict, now groups pages only when the markup those rules read matches too: the same scripts and stylesheets (and whether each carries
integrity), the same<meta>tags with the same viewport, geo and refresh values, and the same number of<main>landmarks. Before, a page missing its viewport meta, carrying a second<main>or loading a different per-route bundle could inherit a sibling's result. Fan-out runs when the rule cache is off: in cloud audits, and in CLI audits withSQUIRREL_RULE_CACHE=0. On a recorded 4000-page gymshark.com crawl, 3931 of 3983 pages still group with their template (3946 before). - Results replayed from an earlier run ignore an
itemsfield that is not a list, so a damaged stored row can no longer crash a smart audit or a publish while it merges results. crawl/all-noindex-pagesvalidates itswarnOnPatternsanderrorOnPatternsoptions. A value that is not a list of strings now fails option validation, instead of reaching the matcher and breaking the rule.perf/source-mapsignores<script>and<style>inside<noscript>. A browser with scripting enabled never runs that markup, so a source map referenced there is no longer reported.content/article-tocfinds a table of contents ItemList nested in a JSON-LD@graphwrapper, as Yoast, Rank Math and Slim SEO emit. Before, the rule reported a missing TOC schema on those sites.
Update to this version
squirrelscan will auto-update, or run this command to update now: