Privacy
Last updated: 2026-05-03
The 30-second version
- We collect only a randomly-generated salt + day-of-last-seen from installed Recipes clients.
- We cannot identify individual users from heartbeat data — this is a mathematical guarantee built into the schema, not a policy promise.
- You can disable telemetry entirely with
RECIPES_TELEMETRY=off— there is no fallback channel. - Account data (email, GitHub/Google ID, Stripe customer ID) is collected only when you explicitly create an account or subscribe.
What the heartbeat actually contains
Every Recipes client (CLI, installer, skill runtime) sends one HTTP request per day to POST /api/v1/heartbeat. The body has exactly two fields:
{
"salt": "<32-character random hex string, generated on first run>",
"last_seen_day": "YYYY-MM-DD"
} Anything else in the request body is rejected with HTTP 400. The server enforces this with a strict Pydantic schema (extra="forbid").
What gets stored on our servers
Our database table fleet_pings has only four columns:
id— synthetic primary keysalt_hash—blake2b(salt, key=server_pepper)— keyed hash, not the salt itselflast_seen_day— the day you sent the pingcreated_at— server-side timestamp (used only for the 90-day TTL job)
There is no column for IP address, user-agent, OS, region, account ID, email, install path, or anything else. The schema cannot be queried for "who installed what" — it can only answer "how many distinct devices were active in week N".
Why we call this "mathematically anonymous"
Two layers:
- Schema: with no PII columns, there's no per-customer query path to expose.
- Hashing:
blake2bis keyed by a server-side pepper. Even if our database were fully exfiltrated, an attacker still couldn't map a hash back to a salt or to a customer without also stealing the pepper from a separate secrets store.
Retention
Heartbeat rows are deleted after 90 days by a weekly cron job. Aggregate weekly counts (e.g. "week of 2026-W18 had N active devices") are kept indefinitely.
Opting out
Set the environment variable on your machine before running any Recipes command:
export RECIPES_TELEMETRY=off Accepted disable values (case-insensitive): off, 0, false, no, disable, disabled. With this set, the heartbeat client short-circuits before any network call. We test this with mocked HTTP transports to assert zero outbound traffic.
Account data
If you create an account or subscribe via Stripe Checkout, we additionally store: your email, OAuth provider ID (GitHub or Google), Stripe customer ID, subscription tier, and (optionally) a Discord user ID if you link Discord for role-gated channels. None of this is shared with third parties beyond the providers themselves (Stripe for billing, Resend for transactional email, GitHub/Google for OAuth).
Questions
Email [email protected].