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 toclaude.aion 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. - 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.jsoninto the App Group containergroup.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
UserDefaultskeysfuneral.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 runningdefaults delete app.cavemode.macos. - Polar license state — your license key and the Polar activation
id are stored in the macOS Keychain under
app.cavemode.licenseso Cavemode doesn't re-prompt on every launch. - Rating-prompt cadence (Mac App Store build only) — small
counters under the
UserDefaultsnamespacereviewPrompt.*(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 runningdefaults 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:
~/.codex/auth.json— read when you connect the account (and again if the access token later lapses) to learn your account id, plan tier (ChatGPT Plus / Pro / etc.), the email on file, and the OAuthaccess_tokenthe Codex CLI already holds. Cavemode copies that access token into the macOS Keychain — one entry per connected account — so it can read your usage. It never writesauth.jsonand never touches a refresh token; token rotation and revocation stay the Codex CLI's job.~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl— Codex CLI writes one of these per conversation. Every turn it appends a rate-limit snapshot (5-hour primary + 7-day secondary buckets, with percentages and reset timestamps) to the file. Cavemode tail-scans the newest file as a local fallback for the account you're currently viewing. 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 and the account's copied access token is deleted from the
Keychain immediately (Cavemode does not, and cannot, revoke the Codex CLI's own
sign-in).
Gemini CLI account (cavemode.app download only, beta)
If you choose to add a Gemini CLI account in Settings, Cavemode reads ~/.gemini/oauth_creds.json — a file the Gemini CLI itself
maintains on your Mac, owned by your user account — to learn your access
token, refresh token, and the email on file so the popover can label the
card. Both tokens are copied into the macOS Keychain (one entry per
connected account). Unlike the Codex CLI integration above, checking your
quota requires outbound requests to Google:
- Cavemode sends your access token to Google's Code Assist API
(
cloudcode-pa.googleapis.com) to read back your remaining usage for the connected Google account, roughly every two minutes while the account is connected. - 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. Personal Google accounts (free, AI Pro, AI Ultra) are not supported by the quota API as of mid-2026 — Cavemode shows an honest message instead of a usage number for those accounts; Workspace and Code Assist Standard/Enterprise accounts work normally.
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 add 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 file reads, 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 — three ways to connect
Cavemode offers three ways to connect a claude.ai account, all reachable from the same "Add account" flow. Only one needs to succeed; you pick whichever is easiest.
- Sign in inside Cavemode (default). Cavemode opens
claude.ai's real login page inside a sandboxed, in-app browser window
(
WKWebView) and, once you sign in, captures the resultingsessionKeycookie automatically — the same cookie your regular browser would receive, just read once at the moment login succeeds instead of you copying it from DevTools. The embedded browser talks only toclaude.ai; Cavemode does not see your password, 2FA code, or anything you type into the page, only the cookie the page sets when login completes. - Import from Claude Code CLI. If you're signed in to the
Claude Code CLI, Cavemode reads its OAuth credential — the Keychain
item
Claude Code-credentials, or failing that~/.claude/.credentials.json, or theCLAUDE_CODE_OAUTH_TOKENenvironment variable — the same credential store the CLI itself reads and writes, owned by your user account and never modified by Cavemode. This is a different token from thesessionKeycookie above 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 anAuthorization: Bearerheader 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
sessionKeycookie from claude.ai's DevTools and paste it into Cavemode. Ships in every build.
Whichever way you connect, the resulting credential is stored in the macOS
Keychain, 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 (sign
in, 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, Codex and Gemini 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:
- The Keychain item
Claude Code-credentials, or failing that~/.claude/.credentials.json(Claude Code) ~/.codex/auth.json(Codex)~/.gemini/oauth_creds.json(Gemini)
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 of the three is present, 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 in Settings, 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. 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)
Cursor offers two ways to connect, both landing on the same cookie:
Cavemode can open cursor.com's real login page inside a sandboxed,
in-app browser window (the same WKWebView capture described
for Claude above) and read back the WorkosCursorSessionToken cookie once you sign in, or you can paste that cookie value yourself from
DevTools. Either way, the cookie is stored in the macOS Keychain and used
to call the undocumented GET https://cursor.com/api/usage-summary endpoint roughly once a minute to read back your included-plan usage and
on-demand spend.
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 sign in or 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.jsonthat 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 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.
- 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. - 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. - To Apple StoreKit — Mac App Store build only. Standard Apple
in-app purchase / restore flow, handled by the operating system. Cavemode reads
Transaction.currentEntitlementslocally 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 matchingsk-ant-sid01-…, Polar license keys, andAuthorization/Cookieheader 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.sessionandapp.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 its credential 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, with no further action needed. 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).