The license and activation fields below are tied to that key. Event and metrics rows are not. Skipping an email means we never learn your name.
The data
- License keypulli-XXXX-XXXX-XXXX-XXXXWith your key
- This is your account. We look up your license and activations with it. No email required.
- EmailoptionalIf you add one
- Only if you attach one. We use it for key recovery, renewal notices, and a receipt for every USDC purchase, renewal charge, or refund. A receipt shows the date, the plan, the amount, the wallet that paid, and the transaction link. Without an email on the key we send no receipts, and the on-chain transaction is the record. Leave it blank and we never see it.
- License typefree or proWith your key
- The app checks this to know which features to unlock. Your key stays the same if you upgrade.
- Coarse countryderived from IPWith your key
- We derive your country from your IP at key generation, then throw the IP away. Powers the install map on this page.
- IP addressrate limiting onlyNot stored
- Never written to our database. The rate-limit counter in Cloudflare's edge key-value store is keyed by an HMAC of your IP rather than the IP itself, and expires within the hour. We read the IP to derive the country above. We don't send it to our payment processor.
- Device idHMAC of your key + MacWith your key
- Derived on your Mac as HMAC-SHA256(license key, hardware UUID). The raw hardware ID never reaches us. The same Mac with a different key produces a different id, so there is no cross-license correlation, and without the key an outsider cannot recompute it.
- Generic device labele.g. "MacBook Pro"With your key
- Helps you tell your devices apart. We store the model name, never your Mac's hostname.
- App versione.g. 1.4.2With your key
- Tells us when to prompt you about an update.
- OS platform, architecture, versione.g. macOS, arm64, 15.4With your key
- If something breaks on a specific macOS build, this helps us find it.
- Last usedtimestampWith your key
- When the app last completed a license validation. Feeds the "active this week" counter. An activation that has not checked in for 180 days is released so the one seat can move to another Mac.
- Plan flagscatalog for the typeWith your key
- What your key unlocks: product id, one-device seat, feature flags, expiry, billing period, and signed auto-renew choice. For crypto purchases we keep the paying wallet address, plan, USDC amount, start and paid-through dates, whether renewal was stopped, a random reference the contract holds for your key, and the transaction hash of each renewal and refund we send. No card or bank details — payment settles on the blockchain.
- Device request tokenrandom, per activationWith your key
- A random token minted when a device activates. Your app signs later requests with it so we can tell a real device from a replayed one. It points at the activation, not at you.
- Eventspage views, downloads, update checks · 24 monthsAnonymous
- Type, source, country, timestamp, and for app events the CPU architecture, release channel, and a small blob like the version you updated from. No license key and no device id. A nightly job deletes rows older than 24 months.
- Daily resource summaryone row per UTC dayIf you share
- One anonymous row per UTC day if you share usage or diagnostics: min / avg / p95 / max for memory (RSS), CPU, thread count, and active download count. No license ID, email, or device id. No URLs, filenames, or bytes transferred.
- Feature preference snapshotsettings flagsIf you share
- Settings flags only: appearance (theme, material, animation, color mode, list view, menubar icon/speed), scheduler (status, retry, completion hook, work hours, prevent sleep), system (launch at login), extension (whether browser integration and skip-fetch-box are enabled in Settings), fetch (parallel downloads, threads, auto-checksum, auto-rename), network (speed limit on/off, proxy on/off), and notifications (enabled). Preference flags only. No install UUID, first-install day, marketing version, live extension-connected flag, proxy host, credentials, paths, or download content.
- Metrics flush timetimestampIf you share
- When the app last uploaded that day's anonymous usage summary. Used for freshness only.
- Diagnostics idrandom, on Axiom linesIf you share
- If you share usage or diagnostics, the app mints a random id and tags core (and extension-relayed) Axiom lines with it so one opt-in session's errors can be read as a sequence. The id lives only on your Mac while those toggles are on. Turning both off deletes it; turning them back on mints a fresh one. It is not your license, not your install id, and not the keyed device id above. Website operator lines do not use this id. Lines expire after 30 days.
- Crash reportone Axiom line per crashOn a share toggle, or once you say yes
- Apple's MetricKit hands us a hard crash on the next launch, never during it: the exception type, why the process terminated, and the ordered app steps and stack frames around it. Shipped raw, then scrubbed for URLs, file paths, emails, and tokens before it lands in Axiom. No downloads, filenames, or transfer sizes. Symbols are never uploaded — we match frames to the archived debug symbols offline, so a report that arrives dropped a frame is gone. The diagnostic-logs toggle is the consent; a usage-only opt-in still gets asked per crash, and that untagged path ships nothing without a yes. Lines expire after 30 days.
- Website operator error linefailed API requestsOn failure, anonymous
- From the pulli.app API Worker: failed request terminals and free-form error lines only (route, status, outcome, timing, machine error kind). Successful requests stay out of Axiom. No license key, account id, device id, diagnostics id, email, or download content. Same Axiom dataset as app logs, tagged source=website so we can tell them apart.
What we don't store
This list covers our server. The app on your Mac stores your download queue and settings locally, which never leaves your device unless you turn on the setting below.
- Any lasting record of your IP address
- Your license key on any analytics row
- Your browsing history
- URLs or filenames of anything you download
- How much you download or transfer speeds
- Proxy host, credentials, or custom user agents
- Payment details (purchases settle on-chain; we never see a card or bank account)
Optional: share usage and diagnostic logs
Settings includes two toggles, both off by default. With both off, the app and browser extension send nothing to Axiom and no daily usage row.
Share anonymous usage data posts the daily resource summary and feature-preference snapshot to our API on Cloudflare (the opt-in fields above), and ships warn/error logs from the app (and, if the extension is installed, sealed extension diagnostics relayed through the app) to Axiom.
Share diagnostic logs is the fuller Axiom firehose for the app and extension (debug and above; never the lowest TRACE noise). It still includes the usage path for the daily summary.
Crash reports are their own consent, and the diagnostic-logs toggle is the one that covers them. When Pulli quits unexpectedly, Apple's MetricKit hands us the technical details on the next launch: the exception, why the process terminated, and the sequence of app steps and stack frames that led there. The report holds no downloads, filenames, or URLs — we scrub the same things we scrub from log lines. Symbolication runs offline against the debug symbols archived per build, so what arrives is all we keep. With diagnostic logs on, crashes ship silently, tagged with that toggle's diagnostics id. With it off — including when only the usage toggle is on — nothing is sent: the app asks you once on the next launch and ships nothing unless you answer yes. That path carries no diagnostics id, because turning the toggles off deleted it.
Metric rows store no license, name, email, or device id. App and extension Axiom lines hold messages, component names, and timing data, tagged with a random diagnostics id minted for that opt-in and deleted when you turn both toggles off. We strip URLs, file paths, emails, tokens, and credentials before anything leaves your Mac. None of this includes what you download or how much. The extension never holds an Axiom token. Turn both toggles off anytime; new uploads stop immediately. Existing metric rows expire after 24 months, and Axiom lines after 30 days. Website operator logs (above) are separate and not gated by these toggles. See our privacy policy for the legal details.