graycat.app
Website Privacy
Short version
The GrayCat website uses first-party statistics to record home-page visits and three app-action events: product-page views (product_view), direct-download starts (download_start), and outbound App Store clicks (app_store_outbound). Full visit, download, action, and home-page geography aggregates are visible only to the site owner; the home page does not publicly display visit totals or any geography data. For directly downloadable apps, a public milestone such as 100+ appears after 100 counted starts. The site sets no analytics cookie, loads no third-party analytics script, and does not store raw IP addresses.
How one visit is counted
When the home page opens in a browser, it sends one request to a same-origin Cloudflare Worker. As with an ordinary website request, the Worker briefly receives the source IP while handling that request. It immediately combines the UTC date and IP with a key stored only as a Cloudflare Secret, calculates a keyed HMAC-SHA-256 daily digest, and discards the raw IP. Without that secret, the digest cannot be directly matched to the original address; this is pseudonymous deduplication, not complete anonymization.
The same public IP is counted at most once per UTC day. This is a network-and-day deduplicated visit count, not an exact person count: households, institutions, VPNs, and mobile networks may share one public IP.
For each first counted visit of the UTC day, the Worker reads the country code, first-level region, and city that Cloudflare estimates from the source public IP, then increments only the corresponding daily aggregate. This is not GPS, street, or residential-address data. VPNs, Tor, mobile networks, corporate gateways, and privacy proxies can make the result describe a network exit rather than the visitor's actual location; missing fields are grouped as “Unknown.” The geography table uses the UTC date as its smallest time unit and stores neither a visit timestamp nor a per-request log; at low traffic, a particular “date + city” aggregate can still have a count of one.
How the three app actions are counted
- Product-page view: after a product page loads, the site script sends one same-origin
product_viewrequest with no body. This event may be absent when JavaScript is disabled, content is blocked, or the network request fails. - Direct-download start: when a client makes a qualifying GET request to an app's latest-version route, the Worker attempts to record
download_startwhile immediately redirecting to the immutable installer. It does not prove that a transfer completed or an app was installed, and excludes automatic updates and direct requests to immutable installer URLs. - App Store outbound: when someone uses an App Store button on this site, a first-party route attempts to record
app_store_outboundwhile immediately redirecting to the original Apple page. This records an outbound click, not an App Store download, purchase, or installation.
Each event type gets a separate keyed digest from the event type, UTC date, app ID, and public IP. The same public IP can count at most once per app, event, and UTC day. Different HMAC input domains are used for different events, so the records cannot reliably connect one visitor's product view to a later action as a personal funnel. HEAD requests, prefetches, prerenders, and known automated link-preview agents are excluded. If the statistics database is unavailable, direct-download and App Store redirects still work.
Campaign source codes
A link may carry a first-party campaign parameter. Metrics accept only producthunt, reddit, v2ex, sspai, bilibili, youtube, xiaohongshu, alternativeto, macupdate, hackernews, email, press, in_app, and unattributed; missing or invalid values become unattributed. A valid code is retained across ordinary same-origin page navigation and passed to this site's direct-download and App Store outbound routes. It is not added to privacy notices, restricted administration pages, APIs, static assets, or off-site links, and is not written to cookies, localStorage, or sessionStorage. For the same public IP, app, event, and UTC day, the campaign on the first counted request is retained and later duplicates do not rewrite attribution. Metrics do not read or store a raw referrer or full visited path.
What is stored
- Visit deduplication records: UTC date, keyed daily digest, Cloudflare-provided continent code, and first-recorded time.
- Home-page geography daily aggregates: UTC date, country code, first-level region code and name, city name, and aggregate visit count; these rows contain no IP, visitor digest, or visit timestamp.
- App-action deduplication records: UTC date, event type, app ID, campaign source code, keyed daily digest, and first-recorded time.
- Cumulative visit, continent, and per-app direct-download-start totals, plus daily aggregates by UTC date, event, app, and campaign; these long-term aggregates contain no personal identifier.
Visit and app-action deduplication records are retained for about 8 days. Home-page geography daily aggregates are retained for up to about 90 days. Both are cleared by an hourly scheduled task; the geography rows are used only to show recent country, state/province, and city distribution. App-action daily aggregates are retained long term to compare sources and action rates.
The first-party metrics database does not store raw IP addresses, street or residential addresses, postal codes, latitude or longitude, precise visit times, per-request location logs, full visited paths, page-view history, raw referrers, cookies, localStorage, sessionStorage, browser fingerprints, advertising identifiers, or user-submitted content. Visit and geography aggregates are not displayed on public pages.
Purpose and public display
The home page does not publicly display visit totals or country, region, or city distribution. Full visit, geography, download, and app-action aggregates are visible only to the site owner. Once an app reaches 100 direct-download starts, public pages show only tiered milestones such as 100+, 500+, or 1K+; nothing is shown below 100. Public endpoints provide neither exact totals, geography aggregates, nor individual event records. Campaign-level daily aggregates are used only to understand which publishing sources lead to product interest, direct-download starts, or outbound App Store clicks.
These data support aggregate action rates, not an exact person-level conversion funnel. Shared public IPs, JavaScript or content blocking, network failures, and daily deduplication all create differences between product-page views and later actions.
Cloudflare
The GrayCat website is served through Cloudflare Workers, Static Assets, and R2, so network requests are processed by Cloudflare. Country, first-level region, and city are Cloudflare IP-geolocation estimates. The counter uses Cloudflare D1 for storage; its digest key is stored as a Cloudflare Secret and is not written to site code or the database.
Apps are unaffected
These statistics run only on the GrayCat website and are not added to GrayCat Markdown, GrayCat Text, GraycatTunes, GrayCat PDF, or KneeDaily. Automatic app updates are not counted as direct website downloads, and this does not change any app's published privacy policy.
Contact
For website privacy questions, email support@graycat.app.