Your code never leaves your machine
SiteCMD is local-first. Your source code never leaves your device, and neither does anything else until you connect a site, which is off until you set it up. You do not have to take our word for it: every network call the app makes is named on this page, and you can watch the traffic yourself.
See how to verify it yourselfScan data stays on your machine
Everything below is stored in a local SQLite database on your device. None of it reaches a SiteCMD server or any third party unless you connect that site, which is the next section.
- Your source code, file paths, and file contents
- Page HTML, response bodies, and screenshots
- Scan results, scores, and issue history, unless you connect the site
- Project names, URLs, and configuration, unless you connect the site
- Core Web Vitals measurements and accessibility results, which stay in the local scan record
- Cookies, storage, or cache shared with your browser or carried over between scans
- Any new window, any download, or any top-level navigation to an internal address
- Any query to a SiteCMD-controlled or hardcoded third-party DNS service
- Scan results, page content, or anything beyond the domain name being looked up
Scanning a site does mean talking to it. These requests go from your machine to the site being scanned and to your own resolver, never to us, and nothing they return is relayed anywhere.
- The scanned page itself, loaded in a hidden private webview to measure Core Web Vitals and, when accessibility analysis is on, run axe-core. The subresources that page then requests - images, scripts, stylesheets, fonts, fetch calls - are fetched by the platform's browser engine, to the hosts the page names, exactly as they would be if you opened it yourself. SiteCMD neither chooses those requests nor can intercept them, and scheduled scans run this layer too
- The scanned site's domain name, as standard DNS queries (SPF/DMARC/DKIM/MX/DNSKEY/CAA, plus a CNAME and address lookup for the www subdomain to catch dangling aliases) to the machine's own configured DNS resolver - the same resolver every other app on the machine uses
The far side of that conversation is documented too. What the scanner requests lists every kind of request a scanned site receives, the identity SiteCMD sends, and how to block it.
Connecting a site, and what that changes
A connected site is the one feature that sends findings to a SiteCMD server. It is off until you connect one, there is no background sync, and an installation that never connects never contacts that host at all.
What travels is a finding's identity, not its evidence: the check, its severity, the route on your site it fired on, and for a code finding a keyed hash of where it fired, computed under a key that stays on your device. Your source, your file paths, and your line numbers have no field to travel in. The row in the table below is the whole list, and the app shows you the same payload before it sends one.
Connecting a site also changes who does the scanning. A connected site can be scanned from SiteCMD's own infrastructure, on a schedule and with the app closed, and those requests leave our network rather than yours. The hosted transport explicitly reaches one service host, marked "SiteCMD's service" in the table below: Cloudflare's public DNS-over-HTTPS resolver, which it asks for address records before every transport or intercepted browser fetch. A full scan also renders the page in Cloudflare Browser Run, so the page's own scripts and markup can request public HTTPS subresources from hosts the page names. The Worker fetches each bounded response through public egress and fulfills it into Chromium. That dynamic class is shown in the hosted-scan row rather than disguised as a fixed destination list. Every such request still crosses the per-request method, resource-type, scheme, port, and private address policy described below.
What a hosted scan sends to your own site, which no capture on your machine can show you because it never crosses your wire:
- The routes you chose for the scan, fetched as ordinary GETs under the same SiteCMD identity a desktop scan sends, but from Cloudflare's network rather than from anyone's laptop. There is no fixed SiteCMD address to allowlist: the source address varies by the location that ran the scan
- A bounded set of probes about the entry route, each one a GET or a HEAD: the favicon and web app manifest the page declares, /privacy and /terms, a deliberately absent path to see how the site answers a 404, the page again with a foreign Origin header to test CORS reflection, the page again without following its redirects to trace the chain, the www variant of the hostname, a sweep of common redirect parameters on a short list of common paths to test for an open redirect, and a sample of the links the page points at, internal and external, to find the broken ones
- On a full profile, a fresh Cloudflare Browser Run context renders each route and can request public HTTPS scripts, stylesheets, images, fonts, XHR, and fetch resources from any host the page itself names. Chromium can request those resources concurrently. SiteCMD intercepts every request, fetches each bounded response through Cloudflare Workers public egress under the same URL and method policy, and fulfills it into the browser. Target hosts receive the SiteCMD identity, not Browser Run's automatic request headers. SiteCMD retains the resulting bounded browser facts, verdicts, and proxy-adjusted timing samples, not the rendered document or response bodies
And the bounds it runs under, which matter more than usual for the same reason:
- Any caller-selected scan target outside the connected, verified site's environment URL and canonical route scope. Browser subresources are page-controlled rather than caller-supplied and still have to pass the per-request public-HTTPS policy
- Response bodies, rendered DOM, or query strings into any store, log, trace, or error report: only derived verdicts and numeric timing samples survive the page step
- Any method but GET, HEAD, and the browser's own OPTIONS preflights, on your origin or any other, so that scanning your page can never become a way of operating it
- Any request over plain HTTP: the hosted address discipline refuses a non-HTTPS URL before it is fetched, which is why the check that asks whether http:// redirects is reported as unanswered from this vantage rather than graded
Operational calls, named and minimal
A small set of calls keeps the app licensed and up to date and resolves your dependencies. Each one sends only what it needs to do its job, never your code or scans, and every one is named in the table below.
Analytics and crash reports, off by default
Both are opt-in and stay off until you turn them on. When enabled, they send only anonymized, sanitized diagnostics, never your scans, source, or credentials. The table below lists exactly what each one carries.
Every call, in one table
Every call SiteCMD can make to a host we can name, by name. The scan traffic that has no host to name, because it goes to your own site and your own resolver, is in the tiers above. The "Sent by" column says whose machine each call leaves, which is the one thing a host name cannot tell you: everything marked "Your machine" is yours to watch with the tools below, and everything marked "SiteCMD's service" only ever happens for a site you connected.
| Call | Sent by | Host | Sends | Never sends |
|---|---|---|---|---|
| License activation | Your machine | api.lemonsqueezy.com | Your license key; A machine-scoped activation identifier: a SHA-256 hash of your device hostname and username encoded as a 16-character hex string prefixed with 'sitecmd-'; the raw hostname and username are never transmitted | Raw hostname or username; Any scan result, source file, project name, or URL |
| License validation | Your machine | api.lemonsqueezy.com | Your license key; The per-activation instance ID issued when you activated | Any scan result, source file, project name, or URL |
| Fix-guide catalog activation | Your machine | activation.sitecmd.com | Your license key; The per-activation instance ID issued when you activated; A random nonce generated for the attempt, so a network retry cannot activate twice | Any scan result, source file, project name, or URL |
| Fix-guide catalog updates | Your machine | catalog.sitecmd.com | An opaque entitlement token issued at catalog activation (not your license key); App version; Installed catalog version and channel; The content hash of the guide pack being downloaded, so a publish landing mid-download cannot hand back a different release | Your license key; Any scan result, source file, project name, or URL |
| Domain expiry lookup (RDAP) | Your machine | rdap.org | The scanned site's registrable domain name (in the request URL), to read the public registration record's expiry date; rdap.org redirects to the TLD registry's public RDAP endpoint | Scan results, page content, credentials, or project configuration |
| PageSpeed Insights (web performance) | Your machine | www.googleapis.com | The URL being measured (the scanned site or a project's environment URL) and the strategy (mobile or desktop), when you open Web Vitals or refresh a dashboard's performance signals; Your optional PageSpeed API key as a query parameter, only if you have stored one to raise Google's rate limit | Scan results, source file content, or project configuration |
| Update check | Your machine | releases.sitecmd.com | Current app version; Operating system / platform identifier | Any scan result, source file, project name, or URL |
| Usage analytics | Your machine | telemetry.sitecmd.com | App version and build channel; Operating system family and CPU architecture; Your subscription tier as a plan name only ("free" for the complete local workbench, or the name of an existing subscription's legacy plan; no license key or billing details); Anonymous install identifier (random UUID, not linked to an account); Event name and workflow status; Aggregate counters such as issue totals by severity | Scan targets, project names, source code, credentials, license keys, page content, raw logs |
| Crash and error reports | Your machine | o4511662343127040.ingest.us.sentry.io | Sanitized error message and exception type; Sanitized stack frame function names and filenames; Sanitized breadcrumb trail (last 30 app events, free-form text scrubbed) | URLs, file paths, emails, tokens, license keys, source snippets, request bodies, raw logs, user identity |
| Connected site sync | Your machine | connect.sitecmd.com | The site's URL, and the alias you give it, when you connect it; Check identifiers, severity, and confidence for each finding a scan produced; The routes on your own site that a web finding fired on; Keyed hashes of the code locations a code finding fired on, computed under a key that stays on your device; Commit SHAs, so the service can tell a current scan from a stale one; Detected framework name and version; Engine and check-registry versions, the capability manifest digest, and which checks the scan actually completed; Timing measurements and the route each was measured on; The issue decisions you made locally, once when you connect: dismissals with their policy, and fixes you claimed | Your source code, file paths, or file contents; Line numbers; Page HTML, response bodies, or screenshots; Dependency lists or lockfiles; Integration credentials |
| Hosted DNS resolution and domain checks | SiteCMD's service | cloudflare-dns.com | The hostname of every URL the hosted transport or isolated browser is about to fetch, resolved over Cloudflare's public DNS-over-HTTPS resolver before each hop as an A and AAAA lookup, so an address in a blocked range is refused before anything is requested. That is your own site's hostnames, plus redirect targets, external links the transport checks, and public subresource hosts named by the page. The browser never opens a target socket: the Worker fetches the response through public egress and fulfills it into Chromium; For the connected site's registrable domain: A, AAAA, SPF, DMARC, common DKIM selector, MX, DNSKEY, CAA, and www CNAME/address questions used by the hosted domain checks | Any name your own site did not lead the scan to: every hostname resolved here came from your scan scope or your page's own markup; Scan results, page content, or which account the scan belongs to |
| Hosted front-end vulnerability check (OSV) | SiteCMD's service | api.osv.dev | Names and exact versions of recognizable front-end libraries exposed in public script URLs, batched in one OSV query; Advisory IDs returned by that batch, to retrieve severity, references, and fixed-version details | Page HTML, the scanned URL, source files, lockfiles, credentials, scan results, or the connected account identity |
| Hosted domain expiry lookup (RDAP) | SiteCMD's service | rdap.org | The connected site's registrable domain in the request URL; rdap.org redirects to the TLD registry's public RDAP endpoint | Page content, credentials, scan results, project configuration, or the connected account identity |
| Dependency and vulnerability lookups | Your machine | registry.npmjs.orgpypi.orgrepo.packagist.orgcrates.iorubygems.orgproxy.golang.orgapi.wordpress.orgupdates.drupal.orgapi.osv.dev | Package names and versions | Your manifests, lockfiles, or source |
Releases are signed, and you can check them
Desktop updates, the CLI archives, and the CLI installer all verify against one minisign public key before anything runs. The key is published here and on the download page, and each release ships a SHA-256 checksum list next to its installers.
- Minisign public key:
RWTtzNh0gmMU/8O1AJBbQbUEy9oD5lpqL/dV0qRqlpsCldfWNWgxr5kE(key idFF14638274D8CCED) - The desktop updater refuses a bundle whose signature does not verify against this key, so a compromised release server cannot push a tampered build.
install.shdownloads the CLI archive, its checksum, and its signature, verifies both against this key, and confirms the binary reports the requested version before it moves anything onto your PATH.
Don't trust us. Watch the traffic.
Run a website scan on a project with no integrations connected, no local code linked, and no site connected, and every outbound connection is one this page names. Besides the site being scanned, a plain website scan reaches your machine's own DNS resolver for the domain's public records, rdap.org for its registration expiry, and api.osv.dev to check the versions of any well-known front-end libraries it finds in the page. Nothing carrying your source or scan results ever reaches a SiteCMD server, and that stays true for findings too until you connect that site. Open Web Vitals afterward and you will also see Google PageSpeed Insights (www.googleapis.com) measure that same URL. When a project has linked code, the dependency and vulnerability lookups send package names and versions, never your source.
Run your own capture
- macOS: enable Little Snitch alert mode, or run a tcpdump session scoped to the SiteCMD process, then start a scan.
- Any platform: capture with Wireshark and watch the scan's destinations. Wireshark filters by host and port, not by process, so quit other networked apps first, or use Little Snitch or tcpdump to scope the capture to SiteCMD.
- During a website scan you will see the target site, plus any integrations you connected. You will not see your source code, and you will not see connect.sitecmd.com until you connect that site yourself.
- When you scan a project with linked code, you will also see the dependency and vulnerability registries from the table above. That is a separate dependency check; it sends package names and versions, never your source.
- At startup you will see the update check (releases.sitecmd.com), and, if you have activated an existing commercial entitlement, license validation (api.lemonsqueezy.com). Finding these confirms this page.
For the full per-endpoint reference, seePrivacy and data.