Last updated: 2026-10-14

Privacy & GDPR

Short version: your usage data stays on your Mac — we run no server that ever receives it. What does leave a device is narrow and named: crash reports (Sentry) and product analytics (PostHog) are off by default and start only if you switch them on, in the app or via the banner on this site; both are hosted in the US. Cloudflare hosts this site and counts page views without cookies or a visitor ID. All three are documented in full below — written to satisfy GDPR, CCPA, and basic human decency.

Data controller

The data controller is the independent developer of Cavemode, reachable at notsu@devpon.com. EU residents may contact this address to exercise their GDPR rights (see "Your rights" below).

Lawful basis (GDPR Art. 6)

  • Consent — you paste your claude.ai session key into Cavemode knowingly. No data processing happens before that action. You can withdraw at any time by quitting the app and deleting the Keychain entry (Keychain Access → search "cavemode" → delete).
  • Contract performance — license verification against api.polar.sh is necessary to deliver the product you purchased.
  • No legitimate-interest processing, no profiling, no automated decision-making.

What Cavemode stores

  • Your Claude session key — stored in the macOS Keychain under the service identifier app.cavemode.session. Never transmitted to any server owned by us. Used only to make requests to claude.ai on your behalf, the same way your browser does.
  • Usage snapshots — an hourly record of your session and weekly utilization percentages, saved locally in your Library/Application Support/Cavemode/ folder so Cavemode can draw the weekly devolution report and project your burn-rate forecast (when you'll reach your limit at the current pace). Capped to the last 7 days. Computed and stored entirely on your Mac, never uploaded.
  • Spend snapshots (pay-as-you-go accounts only) — for each usage poll on a per-token-billed account (e.g. a Codex CLI account), Cavemode appends a small local record — timestamp, cumulative token count, an on-device cost estimate, and the model id — to Library/Application Support/Cavemode/spend_history.json. These records let Cavemode show your spend since local midnight and your recent burn rate. They are computed and stored entirely on your Mac, never transmitted, and capped to the last 7 days. Lawful basis: legitimate interest (on-device usage insight you asked for by connecting a pay-as-you-go account). Cleared automatically when you disconnect the account, and removable any time via the erasure step in "Your rights" below.
  • Each account's last reading. Whenever an account's last reading or its health changes, including when a usage check fails, Cavemode writes that account's latest numbers on your Mac: its 5-hour and weekly percentages, when each resets, when the reading was taken, whether the provider had refused the sign-in, and whether the plan reports no usage cap at all. One small file per account, readable by your user only, Library/Application Support/Cavemode/last_reading.account.<id>.json. When there is nothing left to keep, the file is deleted. It lets an account whose sign-in is asleep keep showing the numbers it last had, with their age, after Cavemode restarts. Stored entirely on your Mac, never transmitted. Lawful basis: performance of the user-requested function (Art. 6(1)(b)), showing the usage of an account you connected. Erased the moment you remove the account, and removable any time via the erasure step in "Your rights" below.
  • App preferences — sound on/off, refresh interval, launch-at-login, floating-widget visibility / content toggles / window position. Stored in UserDefaults.
  • Widget snapshot — no longer written. Older versions with the Notification Center widget wrote a widget-snapshot.json into the App Group container group.app.Cavemode.desktop. The widget has been removed, nothing writes or reads that file anymore, and the erasure step below covers the leftover container from an older install.
  • Minigame best scores — stored locally. Not uploaded.
  • Funeral already-seen marker — when your usage hits 100%, Cavemode plays a short "funeral" animation once. So that quitting and reopening the app doesn't replay it for the same lockout, Cavemode records one timestamp per account under the UserDefaults keys funeral.seen.* — the reset time of the limit window you're waiting on, or, when the API doesn't report one, the time you dismissed it. A timestamp and nothing else: no usage figures, no account name. Lawful basis: contract performance (not repeating an animation you already dismissed). Never transmitted; clear by quitting Cavemode and running defaults delete app.cavemode.macos.
  • Polar license state — your license key and the Polar activation id are stored in the macOS Keychain under app.cavemode.license so Cavemode doesn't re-prompt on every launch.
  • Rating-prompt cadence (Mac App Store build only) — small counters under the UserDefaults namespace reviewPrompt.* (cold-launch count, first-launch date, last-asked timestamp + version, weekly-report view count, post-purchase primer flag, onboarding-completed timestamp, first-paid timestamp). Used only to throttle Apple's system rating sheet — see "App Store rating prompt" below. Never transmitted; clear by quitting Cavemode and running defaults delete app.cavemode.macos.

Codex CLI account (cavemode.app download only)

If you choose to add an OpenAI Codex CLI account in Settings, Cavemode reads files Codex CLI maintains on your Mac, both of which are owned by your user account:

  • auth.json in the account's Codex folder (~/.codex, or another folder you gave Codex with CODEX_HOME, such as ~/.codex-work). Cavemode reads it each time it checks the account's usage, to learn your ChatGPT user and account ids, the email on file, and the OAuth access_token the Codex CLI already holds. It keeps no copy of that token. It never writes auth.json and never touches a refresh token; token rotation and revocation stay the Codex CLI's job. If the folder is signed in to a different ChatGPT login, nothing is sent for this account.
  • ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl — Codex CLI writes one of these per conversation, with token counts and, for a ChatGPT sign-in, that sign-in's rate-limit percentages. Cavemode tail-scans the newest files only for an account connected in API-key mode, which has no ChatGPT sign-in to read usage with, and reads token counts only from turns run in API-key mode: a turn run by a ChatGPT sign-in, and its rate-limit figures, belong to that sign-in, so Cavemode skips it. Nothing is written, nothing is transmitted.

Reading your quota requires one outbound request: Cavemode sends the account's access token to OpenAI's ChatGPT backend (chatgpt.com/backend-api/wham/usage), tagged with that account's ChatGPT account id, to read back its remaining 5-hour and weekly usage — roughly once a minute while the account is connected. This is what lets two different ChatGPT logins each show their own separate numbers. No prompt, conversation, or file content is read or transmitted; the request goes only to OpenAI's own servers, never to a server we operate, and OpenAI's own privacy policy governs how it handles it.

Excluded entirely from the Mac App Store build — the sandbox cannot reach ~/.codex/, so the Codex option does not appear in the App Store download. Lawful basis: performance of the user-requested function (Art. 6(1)(b)) — you explicitly add the account in Settings so Cavemode can show you its usage, and the access token exists solely to fetch that number. Disconnect at any time from Settings; the file reads and usage requests stop the same second (Cavemode does not, and cannot, revoke the Codex CLI's own sign-in).

Gemini CLI account (cavemode.app download only, beta)

New Gemini CLI accounts can no longer be added: Google moved Gemini CLI's personal sign-in to Antigravity, and Code Assist licences are being retired. A Gemini CLI account you added before keeps working as described here until you remove it.

When you added a Gemini CLI account in Settings, Cavemode read oauth_creds.json in ~/.gemini (or, when Cavemode itself runs with the GEMINI_CLI_HOME environment variable set, in .gemini inside that folder, the same place the Gemini CLI uses). The Gemini CLI itself maintains that file on your Mac, owned by your user account; Cavemode read it to learn your access token, refresh token, and the email on file so the popover can label the card. Both tokens were copied into the macOS Keychain (one entry per connected account). Cavemode reads that file again only when you choose Check again on that account, and once at launch for an account added before Cavemode kept its own copy of the tokens, to make that copy. Unlike the Codex CLI integration above, checking your quota requires outbound requests to Google:

  • Roughly every two minutes while the account is connected, Cavemode sends your access token to Google's Code Assist API (cloudcode-pa.googleapis.com) twice, as the Gemini CLI does: first to ask which Code Assist tier the account has and which Google Cloud project it uses, then, only when a project is known, to read back that project's remaining usage. The project comes from Google's own answer to the first request, or from the GOOGLE_CLOUD_PROJECT environment variable (or, when that is not set, GOOGLE_CLOUD_PROJECT_ID) that Cavemode itself runs with. A project from either variable goes in both requests. The project is never stored. Cavemode never asks Google to change anything about your account.
  • When that access token expires, Cavemode exchanges the refresh token for a fresh one via Google's token endpoint (oauth2.googleapis.com/token) — byte-for-byte the same call the Gemini CLI itself makes on its next run, using the CLI's own public installed-app client id. This keeps the quota reads working without you re-signing-in.

No other file content, prompt, or conversation data is read or transmitted; these requests go only to Google's own servers, never to a server we operate, and Google's own privacy policy governs how it handles them. Google serves this usage only to accounts with a Gemini Code Assist Standard or Enterprise licence. For a personal Google account (free, AI Pro, AI Ultra), or an account Google turns away for another reason, Cavemode shows a plain message saying why and what to do next instead of a usage number.

Excluded entirely from the Mac App Store build — the sandbox cannot reach ~/.gemini/, so the Gemini CLI option does not appear in the App Store download. Lawful basis: performance of the user-requested function (Art. 6(1)(b)): you explicitly added the account in Settings so Cavemode can show you its usage, and the tokens exist solely to fetch that number. Disconnect at any time from Settings; the quota requests and token refreshes stop the same second and both copied tokens are deleted from the Keychain immediately (Cavemode does not revoke the Gemini CLI's own sign-in).

Claude account: two ways to connect

Cavemode offers two ways to connect a claude.ai account, both reachable from the same "Add account" flow. Only one needs to succeed; you pick whichever is easiest. Cavemode has no sign-in window of its own: you sign in to claude.ai in your own browser.

  • Import from Claude Code CLI. If you're signed in to the Claude Code CLI, Cavemode reads the sign-in Claude Code saved for that config folder each time it checks the account's usage: the Keychain item Claude Code-credentials for ~/.claude, or Claude Code-credentials- plus eight letters and digits Claude Code derives from the folder, for each folder you gave it with CLAUDE_CONFIG_DIR (such as ~/.claude-work), or failing that the folder's .credentials.json. It also reads the email and organization id in the folder's identity file, to label the account and to make sure it never sends one login's token for another: ~/.claude.json for the default ~/.claude, and .config.json or, failing that, .claude.json inside each folder you gave Claude Code with CLAUDE_CONFIG_DIR. If that file names no account, nothing is sent. Reading the Keychain item never shows a macOS dialog by itself: if macOS wants your permission first, the account says so and asks you to press Allow access, and the dialog appears only then, or when you connect the account. This is the same credential store the CLI itself reads and writes, owned by your user account and never modified or copied by Cavemode. It is a different token from the pasted sessionKey cookie below and can't be converted into one, so checking its quota calls a different endpoint: api.anthropic.com/api/oauth/usage, sending the token as an Authorization: Bearer header the same way the Claude Code CLI itself does. Excluded from the Mac App Store build — the sandbox cannot reach ~/.claude/ or that Keychain item.
  • Paste a session key manually. The original flow — copy your sessionKey cookie from claude.ai's DevTools and paste it into Cavemode. Ships in every build. If an account reads a Claude Code folder and you switch it to a pasted code, Cavemode stores that code the same way and stops reading the folder; switching back deletes the stored code.

A pasted code is stored in the macOS Keychain; a Claude Code sign-in stays where Claude Code keeps it and is read from there. Either is used only to call claude.ai or api.anthropic.com on your behalf, and never transmitted to any server we operate. Lawful basis: performance of the user-requested function (Art. 6(1)(b)) — you take an explicit action (import or paste) to connect each account, and the credential exists solely to fetch the usage numbers you asked to see. Disconnect any time from Settings; see "Your rights" below for the full erasure path.

Looking for sign-ins already on your Mac (opt-in)

Claude Code and Codex each save their own sign-in on your Mac when you use them. Cavemode can look for those, so that connecting an account is one tap instead of a copy-and-paste. It asks first. The first time you open the connect screen, Cavemode shows a plain-language disclosure explaining what it would look at, and reads nothing until you answer. Declining is a complete answer — the manual paste and sign-in paths all still work.

If you allow it, Cavemode reads only these locations, all of which belong to your own user account and are written by those apps themselves:

  • ~/.claude.json for the default ~/.claude, and in every other folder in your home whose name starts with .claude, its .config.json or .claude.json and its .credentials.json (Claude Code)
  • auth.json in every folder in your home whose name starts with .codex (Codex)
  • A Claude Code Keychain item (Claude Code-credentials, or its per-folder variant) only when two folders are signed in to the same account, to show you the one whose sign-in is freshest, and only if macOS lets Cavemode read it without asking you; it never shows a dialog for this

Once you have allowed it, an account's Setup page also looks for a login that is not tracked yet while it is open, each time one of those folders or files changes on disk, reading the same files but never the Keychain.

The read is read-only — Cavemode never writes to, moves, or signs out of another program's credential store, and never consumes the rotating refresh tokens those files hold. What it finds (which folders hold a sign-in, and the email or account label inside, where there is one) is shown to you on screen so you can confirm or deselect each one before anything is connected. None of it is transmitted anywhere — not to us, not to any third party. The result is held in memory while the screen is open and is gone when you close it; nothing about what was found is written to disk.

What is stored: exactly one value — your yes/no answer, as the string allowed or declined, under the connect.localSignInScan.decision preference key. It records whether we may look; it cannot record what was found. Lawful basis: consent (Art. 6(1)(a)), given by your explicit answer and withdrawable at any time in Settings → Privacy. Excluded from the Mac App Store build entirely — that build's sandbox cannot read another app's files, so it says so plainly instead of showing an empty result.

OpenRouter account

If you paste an OpenRouter API key when you add an account, Cavemode stores it in the macOS Keychain (one entry per connected account) and calls GET https://openrouter.ai/api/v1/key with your key as an Authorization: Bearer header — roughly once a minute while the account is connected — to read back your remaining balance or rate limit. Nothing else is sent to OpenRouter; Cavemode never reads or transmits your prompts, model choices, or request history. Cavemode keys the account by a one-way digest of the key, never the key itself, and shows the key's label as its name. Accounts added by earlier versions, which were keyed by the label, move to the digest the first time this version starts. Ships in every build (plain HTTPS, no filesystem access needed). Lawful basis: performance of the user-requested function (Art. 6(1)(b)) — you paste the key yourself so Cavemode can show you your own OpenRouter usage. Disconnect any time from Settings; the key is deleted from the Keychain immediately.

GitHub Copilot account (beta, cavemode.app download only for now)

If you connect a GitHub Copilot account, Cavemode starts GitHub's OAuth Device Flow (RFC 8628): it requests a device code from github.com/login/device/code, shows you a one-time code and a GitHub verification link, and polls github.com until you approve it in your own browser. GitHub then issues a bearer token, which Cavemode stores in the macOS Keychain and uses to call the undocumented GET https://api.github.com/copilot_internal/user endpoint every couple of minutes to read back your Copilot plan and quota. In plain terms: no password, no browser embedded inside Cavemode for this one — you approve the connection on GitHub's own site.

Honest disclosure: because GitHub does not offer a public OAuth client id for third-party Copilot-quota tools, this device flow authenticates using the OAuth client id GitHub's own VS Code Copilot Chat extension uses — the only client id GitHub's undocumented copilot_internal/* endpoints currently accept for this kind of read. It is not an app we registered ourselves, and GitHub could revoke or change that access at any time; a kill switch (FeatureFlags.copilotProviderEnabled) lets us pull the feature without an app update if that happens. This is disclosed here so you can make an informed call about connecting a Copilot account. Lawful basis: performance of the user-requested function (Art. 6(1)(b)) — you explicitly start and approve the device-flow connection so Cavemode can show you your own Copilot usage. Disconnect any time from Settings; the token is deleted from the Keychain immediately.

Cursor account (beta, cavemode.app download only for now)

You connect Cursor by signing in at cursor.com in your own browser and pasting the value of its WorkosCursorSessionToken cookie into Cavemode. Cavemode checks it once with GET https://cursor.com/api/auth/me, which also gives the email it shows as the account's name, then stores it in the macOS Keychain and uses it to call the undocumented GET https://cursor.com/api/usage-summary endpoint roughly once a minute to read back your included-plan and on-demand usage against their limits, your plan type, and when the billing cycle ends. When the plan reports no usage cap, Cavemode says so instead of showing a percentage.

Honest disclosure: that endpoint is a private dashboard API, not a published Cursor API — its shape has changed before and could change again without notice, which is why this connection carries a "Beta" label in the app and defensive parsing that degrades to an honest "unavailable" state rather than showing a wrong number. A kill switch (FeatureFlags.cursorProviderEnabled) lets us pull the feature if Cursor's endpoint stops working entirely. Lawful basis: performance of the user-requested function (Art. 6(1)(b)) — you paste the cookie yourself so Cavemode can show you your own Cursor usage. Disconnect any time from Settings; the cookie is deleted from the Keychain immediately.

AI agent activity bubbles (cavemode.app download only)

The floating widget can show what your local AI coding agents are doing — working, finished, or waiting for your approval — and let you answer Claude Code permission prompts from the widget. To do this, Cavemode:

  • adds hook entries to ~/.claude/settings.json that point at a small helper bundled with the app. The helper forwards Claude Code's hook events (session id, working directory, tool name and input, the last assistant message excerpt) over a private local socket to the Cavemode app on the same Mac. Nothing is transmitted off the device, stored to disk, or added to analytics — the events live in memory only while the session runs.
  • tail-reads Claude Code transcript files and Codex CLI rollout files to show a short excerpt of the latest result in the bubble. Read-only, local-only.
  • while an agent is actively working and the widget is visible, checks how fast its transcript file is growing — a byte-count only, no file content is read for this — about once a second, so the character can “type along” and speed up when output flows fast. The count is used in memory to pick an animation and is never stored or transmitted. Turn off “Typing reaction” in Settings to stop this sampling.

This is on by default so the widget works out of the box, and the Settings pane discloses it next to the toggles. Turn off “Show AI coding activity” in Settings and Cavemode removes its hook entries from ~/.claude/settings.json the same second — that is also the full erasure path, since no agent data is ever persisted. Excluded entirely from the Mac App Store build (the sandbox cannot reach ~/.claude/). Lawful basis: legitimate interest (Art. 6(1)(f)) — narrowly scoped to rendering your own local agents' status on your own screen, with a one-click opt-out. The three related analytics counters (hooks installed / removed, approval decided) carry no content and ship only if you separately opt in to product analytics (off by default).

Accelerometer reads (Slap Attack minigame only)

On Apple Silicon MacBooks (M1 Pro and later), the Slap Attack minigame reads the laptop's built-in motion sensor so a real physical slap can trigger a hit. Data is processed locally and discarded after each sample — nothing is written to disk, nothing is transmitted, and the sensor is only opened while the minigame is on-screen. Desktop Macs and other Apple Silicon models without the sensor fall back to click input with no motion access.

Network requests

Every install makes at most three kinds of network requests out of the box. Connecting an optional provider account (Codex, Gemini CLI, OpenRouter, GitHub Copilot, Cursor, or the Claude Code OAuth import tier) adds the request kinds documented in that provider's own section above — usually a single usage read, though Gemini CLI makes two reads per check and also refreshes its access token against Google's token endpoint when it expires. Each one only starts after you explicitly connect that account, and stops the moment you disconnect it.

  1. To claude.ai — to read your own usage, every 60s, using the session key you provided during onboarding. Plus one immediate fetch the first time you paste a session key, so Cavemode can show your current devolution stage before you ever close the onboarding window. This is identical to what your browser does when you open Claude.
  2. To api.polar.sh — cavemode.app download only. Once at first launch to activate your license key, and a lightweight re-check roughly every 30 days so Polar can revoke refunded or shared keys. The request contains the license key, a device label (your Mac's hostname, e.g. Johns-MacBook-Pro), and an activation id; no user-identifying data beyond that. Excluded entirely from the Mac App Store build.
  3. To Apple StoreKit — Mac App Store build only. Standard Apple in-app purchase / restore flow, handled by the operating system. Cavemode reads Transaction.currentEntitlements locally to verify the lifetime unlock; Apple processes payment and card data under its own privacy policy. Excluded entirely from the cavemode.app download.

No default-on analytics, crash reporting, or telemetry. The two optional toggles in Settings → Privacy & diagnostics (described in the next section) are the only paths by which anything can leave your device — and both are off out of the box.

App Store rating prompt (Mac App Store build only)

On Mac App Store builds, Cavemode may ask the operating system to display Apple's standard rating prompt after positive in-app moments — a new Slap Attack personal best or a return visit to the weekly devolution report. The prompt is rendered and rate-limited by macOS (Apple caps it to 3 prompts per 365 days, system-wide); we do not transmit any rating data and have no way to observe whether you rated, dismissed, or how many stars you chose. Cavemode only knows that it asked the system to show the prompt.

You can also tap "Rate Cavemode on the App Store" in Settings at any time to open the App Store review page directly — useful when the automatic moments don't catch you, since the user-initiated path bypasses Apple's 3/365 cap. The prompt is excluded entirely from the cavemode.app download (which has no App Store listing to rate).

Feedback email

If you tap "Grunt at us" in Settings → About, choose "ME HAVE NOTES" on the occasional "How's it going?" card, or use the /feedback page on this site, Cavemode opens your own mail app with a prefilled message to notsu@devpon.com. Nothing is sent until you press send in your mail client, and the message travels through your email provider — never through a Cavemode server or web form.

From the app, the message includes a small, non-identifying context footer to help us act on bug reports: app version and build, build channel (Mac App Store or cavemode.app), your macOS version, the current devolution stage (0–5), how many accounts you've connected (a count, not identities), and your locale. It never includes your name, your Claude or Codex account, your session key, your license key, or any file path — and you can edit or delete any of it before sending.

Lawful basis (GDPR Art. 6): consent — you choose to write and send the email. We process your email address and message only to read and reply, and keep it no longer than needed to resolve your point. Erasure: reply asking us to delete the thread, or email notsu@devpon.com, and we'll remove the correspondence.

Software updates (Polar.sh distribution only)

The cavemode.app download channel needs its own update path because it ships outside the Mac App Store. We use Sparkle — the open-source macOS update framework — and serve a signed appcast at https://cavemode.app/appcast.xml.

  • Off by default. Settings → Updates → "Automatically check for updates" is the toggle. Until you turn it on, Cavemode never fetches the appcast on its own. Pressing "Check for Updates…" performs exactly one request, and is the only request that runs while the toggle is off.
  • What gets sent. A standard HTTP GET to the appcast URL with a User-Agent string identifying the app, its version, and the Sparkle library version (format: Cavemode/1.0.0 Sparkle/2.x.y). No analytics, no identifiers, no usage data, no IP-derived profile. Cloudflare Pages serves the file and keeps standard access logs (IP, timestamp, status code) under its own privacy policy; we do not query or correlate them.
  • Lawful basis. Explicit consent (Art. 6(1)(a)) — you flip the toggle. Disable it at any time and the requests stop the same second.
  • Mac App Store builds. The Updates section in Settings is informational only — it states that updates flow through the App Store and offers a "Check the App Store" button that hands off to Apple's Mac App Store app. Cavemode itself makes no update-related network requests on the App Store binary; Sparkle is not compiled into it and the appcast URL above is never contacted from that build.

Optional diagnostics & product analytics (opt-in, off by default)

Settings → Privacy & diagnostics exposes two toggles, both defaulting to off. Turning them on is the only way any diagnostic or product event leaves your device. Toggle them off at any time and the outbound pipe stops immediately.

  • Anonymous crash reports — when on, Cavemode routes symbolicated crash traces, main-thread hang reports, and error breadcrumbs to a crash-reporting processor. Every outbound payload passes through an on-device redaction pass that strips /Users/<your-name> paths, URL query strings, Claude session keys (sk-ant-sid and a version number), Polar license keys, and Authorization / Cookie header values. Each report is tagged with the same coarse, non-identifying context as product events — app version + build, build channel, macOS version, Mac model family, language/region, and entitlement tier — to help triage crashes. No email, IP, or device serial is sent.
  • Anonymous usage stats — when on, Cavemode sends a small set of typed product events (onboarding completed, paywall viewed, minigame started, share exported, stage changed, …) to a product-analytics processor. Each event is tagged with a random device UUID generated locally on first opt-in and reset the moment you opt out. Event properties are typed enums only — no free-form strings, no file paths, no license keys, no session keys. Each event also carries a few coarse, non-identifying context fields — app version + build number, build channel (App Store vs direct), macOS version, Mac model family (e.g. MacBookPro18,3), system language/region (e.g. en_US), how many accounts you've added, and your entitlement tier (free vs paid). No precise location, no serial number, no hostname.

Active processors: Sentry (US region, ingestion at *.ingest.us.sentry.io) for crash + error reports; PostHog (US cloud, ingestion at us.i.posthog.com) for product analytics. Both operate under signed Data Processing Agreements (GDPR Art. 28). Sentry Inc. and PostHog Inc. are both U.S.-based, so telemetry — when you opt in — is transferred outside the EEA. We rely on each vendor's published transfer mechanism (Standard Contractual Clauses and/or EU–U.S. Data Privacy Framework certification) for that transfer; current status for each vendor is on their respective processor pages. Every outbound payload first passes through a local redactor (PrivacyScrubber) that strips file paths, Claude session keys, Polar license keys, and URL query strings — audited rule-by-rule in the open-source test suite.

The macOS toggles default OFF. While off, the SDKs never start and zero outbound calls are made. Flipping a toggle on lets the corresponding SDK initialize within the running process; flipping it off calls SentrySDK.close() + PostHogSDK.optOut() so mid-session changes take effect immediately. The toggles persist as booleans under UserDefaults keys diagnostics.crashReportingEnabled / diagnostics.productAnalyticsEnabled.

After a successful purchase, we identify the now-paid user with SHA-256(license key) — never the raw key, never email — so events from a known-paid install can be grouped without storing PII. The same hash is set as Sentry's user.id for crash deduplication. Anonymous installs use a UUID stored in macOS Keychain under service app.cavemode.analytics.

Lawful basis: consent (GDPR Art. 6(1)(a)). Withdraw at any time by flipping the toggle off. Retention (planned): crash reports 90 days, product events 12 months. Right to erasure (GDPR Art. 17): email notsu@devpon.com with the anonymous device UUID shown under Settings; we submit deletion requests to both processors within 30 days and reply when they confirm.

Cookies + third-party services on this website

This site (cavemode.app) is a pure static SvelteKit build hosted on Cloudflare Pages. It loads no fonts from external CDNs. Your browser's localStorage stores two keys — theme (light/dark preference) and cavemode_consent_v1 (your cookie-banner decision: {analytics:bool, decided:bool}). The language switcher also stores a functional first-party PARAGLIDE_LOCALE cookie so your selected language wins over browser auto-detection on future visits. These stay on your device.

On first visit you see a cookie banner. Clicking Reject leaves the site with zero outbound observability requests. Clicking Accept activates the same two processors used by the macOS app: PostHog (autocapture clicks/forms, page views — all text content and element attributes masked before send) and Sentry (error reports plus performance traces sampled at 10%; replays activate only when an error is captured, with all text masked and media blocked). PostHog stores a first-party cookie (ph_*) plus localStorage entries for distinct_id continuity. Both SDKs send to US endpoints (us.i.posthog.com, *.ingest.us.sentry.io) under the EU–U.S. Data Privacy Framework adequacy decision. You can revoke at any time by clicking Reject in the banner (re-summon it via DevTools → Application → Local Storage → delete cavemode_consent_v1).

Clicking a "Download" button on this site pulls the macOS DMG directly from dl.cavemode.app/Cavemode-latest.dmg — a Cloudflare R2 object-storage subdomain we operate alongside the marketing site (dl. is a CNAME we control inside the same Cloudflare account; no third-party CDN is involved). The same Cloudflare access-log disclosure above covers it. Before the click, the button performs a cross-origin HEAD probe to confirm the DMG is reachable; if the probe fails (a deploy gap, a network error), the button relabels itself and points at Apple's Mac App Store listing instead. The app is free on both routes — this site has no checkout, and no button on it can send you to a payment page.

Following the App Store link opens apps.apple.com in a new tab, where Apple's own privacy policy applies. Nothing about you is passed in that link beyond the app id, and the website loads no Apple code, fonts, or pixels — the "Download on the Mac App Store" badge is an SVG we host ourselves.

Cloudflare Pages, our static host, processes standard IP-level access logs for DDoS protection and diagnostics under their processor agreement (cloudflare.com/privacypolicy). These logs are not correlated with any Cavemode-specific data.

Cloudflare Web Analytics. We use Cloudflare's privacy-first Web Analytics to count aggregate page views per URL — for example, how many people open cavemode.app/from/death after a friend shares a death certificate. It is cookieless: it sets no cookies, writes nothing to localStorage, assigns no visitor ID, and builds no cross-site or cross-visit profile. Because it runs entirely on Cloudflare — already our hosting processor under the agreement above — it introduces no new data exporter: the same edge that serves the page does the counting, and the IP is used transiently to derive a bot-filtered visit count, never stored against you. On that basis (data minimisation, no device identifier) it runs under legitimate interest without the consent banner, separate from the opt-in PostHog/Sentry processors described earlier. Cloudflare's analytics privacy notes: cloudflare.com/web-analytics.

Share links and attribution

When you tap a Share button inside Cavemode (death certificate, weekly report, current-usage snapshot, or Slap Attack receipt), the link embedded in your caption is a cavemode.app URL decorated with ?utm_source, utm_medium, utm_campaign, and ref query parameters. The ref value is an 8-character random token generated on your Mac the first time you share. The token is opaque and contains no personal data. The website does nothing with it: there is no checkout for it to flow into (the app is free), so the parameters are simply ignored, and the only measurement of a shared link is the cookieless, aggregate page-view count Cloudflare keeps for the /from/… URL itself.

The share image itself carries a small QR code and a printed cavemode.app address in its footer, so a screenshot or a caption-stripped repost can still lead someone back to the app. The QR encodes exactly the same cavemode.app/from/<card> URL with the same opaque ref token and utm parameters described above (its medium is tagged card) — it is generated entirely on your Mac with Apple's built-in QR generator, contains no personal data, and adds nothing new that leaves your device. Those links now open a matched landing page (for example /from/death) instead of the generic homepage, which is the same static site covered above.

When a visitor lands on cavemode.app carrying those query parameters, the site stores nothing. It used to stash them in the browser's sessionStorage under the key cavemode_ref so a checkout could carry the attribution; with the app free there is no checkout, so the stash had no purpose and was removed — a store with no use is exactly the kind of collection data minimisation forbids. No cookies and no sessionStorage entries are written for attribution, and nothing about the visit is transmitted to us.

You can reset your own ref token any time by running defaults delete app.cavemode.Cavemode app.cavemode.shareId in Terminal. The next share regenerates a fresh token. The Instagram mechanic also writes share images to ~/Pictures/Cavemode/ on your Mac so you can AirDrop them to your phone — drag them to Trash any time to delete.

Adaptive menu bar — what runs locally

To survive the MacBook notch on small screens, Cavemode reads NSScreen.safeAreaInsets on launch and observes the NSWindow.didChangeOcclusionStateNotification stream for the menu-bar icon's window. Both signals are read-only system APIs scoped to your own user session — nothing about your display configuration leaves your Mac. When Cavemode detects the icon is clipped, it locally posts a macOS notification and narrows the menu-bar title; no event is sent anywhere unless you have opted into product analytics, in which case a single menu_bar_auto_compacted event records that the heuristic fired.

Cavemode also registers a system-wide keyboard shortcut (⌃⌥⌘C by default) so you can open the popover even when the menu-bar icon is hidden. The shortcut is registered via macOS's Carbon RegisterEventHotKey API and is processed entirely on your Mac. No accessibility permission is requested.

Cavemode ships a small companion process — CavemodeMenuBarHelper (app.cavemodeapp.menubar) — embedded inside the main app bundle at Cavemode.app/Contents/Library/LoginItems/. It runs alongside the main app and owns the actual menu-bar icon. The split exists because macOS's menu-bar slot allocator can deny new icons for an entire bundle-id prefix; routing the icon through a separate prefix is the only reliable workaround. The helper is registered as a Login Item via macOS's SMAppService API on first launch (you may see an approval prompt in System Settings → General → Login Items). It communicates with the main app entirely through a local NSXPCConnection (mach service group.app.cavemode.shared.menubar) — no network access, no Keychain access, no file access. The connection is scoped to your own login session via a shared App Group entitlement. If you decline the Login Item, the main app continues to run; only the menu-bar icon goes away.

Your rights (GDPR Art. 15–22)

  • Right of access & portability — Cavemode stores nothing on any server. The data on your device (Keychain + local files) is already in your possession; export it via macOS tools at any time.
  • Right to rectification — re-enter your session key in Cavemode's settings.
  • Right to erasure ("right to be forgotten") — quit Cavemode, delete the Keychain entries app.cavemode.session and app.cavemode.license, and drag ~/Library/Application Support/Cavemode — plus, if you ever used the old Notification Center widget, the leftover App Group container ~/Library/Group Containers/group.app.Cavemode.desktop — to the Trash. Deleting the app removes them automatically. Nothing remains. For Polar order data, contact Polar directly.
  • Per-account erasure, without deleting the app. Every connected provider account (Claude, Codex, OpenRouter, GitHub Copilot, Cursor, Gemini CLI) keeps any credential Cavemode stores in its own Keychain entry. Removing that account from Cavemode's account list — "Remove account" in Settings or the account rail — deletes its Keychain entry immediately, the same second, and erases its saved last reading with it. This is the in-app deletion path for any single provider connection; you don't need to uninstall Cavemode or contact us to disconnect one account.
  • Erasing your answer about looking on this Mac. Settings → Privacy has a "Look for sign-ins on this Mac" switch and a "Forget my answer about looking on this Mac" button. The switch withdraws consent — Cavemode stops looking immediately. The button goes further and deletes the stored answer outright, so afterwards the app holds no record that the question was ever asked and will ask again next time. That one preference value is the only thing this feature ever writes, so removing it is a complete erasure; accounts you already connected are unaffected and are removed separately, per the bullet above.
  • Right to object, right to restrict processing — stop using the app; no processing continues.
  • Right to lodge a complaint — with the supervisory authority in your EU member state.

Data transfers

The app calls claude.ai (Anthropic PBC — USA) and api.polar.sh (Polar Software Inc. — USA) using your authenticated session. These are direct peer-to-peer calls from your Mac; Cavemode does not proxy, log, or observe them. Both providers have published data-transfer mechanisms (SCCs) for EU↔US flows.

Retention

Local files rotate according to your usage history settings (default: 7 days of snapshots). All retention happens on your device. The app never sends retention metadata anywhere.

Children

Cavemode is a developer tool. We do not knowingly collect or process data from anyone under 16.

What Cavemode never does

  • Send your prompts, conversations, or responses anywhere
  • Log what you type into Claude
  • Read Claude's web interface DOM
  • Share your session key with anyone, ever
  • Install kernel extensions, system services, or anything else invasive

Changes to this policy

Material changes (new data categories, new third parties, new retention periods) will be announced in the in-app release notes and on the changelog at least 14 days before taking effect. Minor wording updates bump the "Last updated" date above.

Questions?

Email notsu@devpon.com — replies within 72 hours on weekdays. GDPR data-subject requests are honoured within 30 days (Art. 12).