What we store

Every field on our server, in plain language.
No more, no less.

Pulli doesn't use usernames or passwords. When you sign up, we generate a random account key (like pulli-XXXX-XXXX-XXXX). That key is your account. Everything below is linked to it, not to your name or email. If you never attach an email, nothing on our server connects your account to you.

The data

License keypulli-XXXX-XXXX-XXXX
This is your account. We look up your license and activations with it. No email required.
Emailoptional
Only if you attach one. We use it for receipts and account recovery. Leave it blank and we never see it.
License typefree or pro
The app checks this to know which features to unlock. Your key stays the same if you upgrade.
Coarse countryderived from IP
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 only
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, and we pass it to Polar at checkout so they can work out currency and tax. Nothing else.
Device idHMAC of your key + Mac
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"
Helps you tell your devices apart. We store the model name, never your Mac's hostname.
App versione.g. 1.4.2
Tells us when to prompt you about an update.
OS platform, architecture, versione.g. macOS, arm64, 15.4
Diagnostics. If something breaks on a specific macOS build, this helps us find it.
Last usedtimestamp
When the app last completed a license validation. Feeds the "active this week" counter. Independent of anonymous usage sharing.
Plan flagsfree or pro
What your key unlocks: product id, device allowance, feature flags, expiry, and a marker for whether Pro came from a subscription or a one-time purchase. No payment details, ever.
Device request tokenrandom, per activation
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
Type, source, country, timestamp, and for app events the CPU architecture, release channel, and a small blob like the version you updated from. No event row carries your key. App events carry a device pseudonym instead — an HMAC of your machine ID and the current month, under a secret only our server holds. The value changes every month, so we can count distinct devices this month without joining them to last month's rows.
Daily resource summaryopt-in · Share anonymous usage data
One anonymous row per UTC day when the toggle is on: 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 snapshotopt-in · Share anonymous usage data
Settings flags only: appearance (theme, vibrancy, animation, color mode, list view, menubar icon/speed), scheduler (status, retry, completion hook, work hours, prevent sleep), system (launch at login), extension (integration, skip fetch box), fetch (parallel downloads, threads, auto-checksum, auto-rename), network (speed limit on/off, proxy on/off), and notifications (enabled). No proxy host, credentials, paths, or download content.
Metrics flush timeopt-in · timestamp
When the app last uploaded that day's anonymous usage summary. Used for freshness only.
Diagnostics idopt-in · sent to Axiom
When either diagnostic toggle is on, the app mints a random id and tags every Axiom log line with it so one opt-in session's errors can be read as a sequence. The id lives only on your Mac while diagnostics are on. Turning both toggles 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, so we cannot join Axiom lines to an account. Lines expire after 30 days.

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 (Polar.sh handles billing as our merchant of record)

Optional: share anonymous usage data

Settings includes a "Share anonymous usage data" toggle, off by default. When you turn it on, the app sends two things: crash and error logs to Axiom, and a daily resource summary plus feature-preference snapshot to our API on Cloudflare (the opt-in fields above). Metric rows store no license, name, email, or device id. Axiom lines hold error messages, component names, and timing data, tagged with a random diagnostics id minted for that opt-in and deleted when you turn diagnostics off, so they cannot be joined to your license. We strip URLs, file paths, emails, tokens, and credentials before anything leaves your device. None of this includes what you download or how much. Turn the toggle off anytime; new uploads stop immediately. Existing metric rows expire after 24 months on their own, and log lines already in Axiom expire after 30 days. A separate "Share diagnostic logs" toggle sends the full debug trace to Axiom when you ask us to help debug a problem. See our privacy policy for the legal details.

Install activity

Total installs
Active this week
Countries reached