Privacy & Data
Oddlytics runs entirely on infrastructure you control. Odd Omens doesn't run a server for it and never receives your events.
What the App Sends
The Swift package sends only the events you track. Each event carries the name and metadata you pass, plus a few fields it adds itself: the appId, a per-launch session ID, a user ID, a device ID, the platform and the time. On iOS, the user ID and device ID are both the device's identifier for vendor. The full list is in What Is Collected Automatically.
Events go to one place: the Worker address you pass as endpoint, over HTTPS if that address uses it. The package doesn't contact anything else.
It doesn't collect the device model, OS version, locale, location, advertising identifier, contacts or any other personal data, and it asks for no permissions. Whatever you put in metadata is sent as is, so leave out names, emails and other personal details.
What the Server Stores
The Worker stores each event as one row in your D1 database:
| Column | Content |
|---|---|
event_name | The event name. |
app_id | The app's appId. |
timestamp | When it was tracked. |
platform | iOS from the Swift package, or what another client sends. |
metadata | Your metadata as JSON. |
session_id | The launch's session ID. |
user_id | The user ID. |
It doesn't store the device_id field or the sender's IP address. Two more tables hold dashboard settings only: event_aliases (event display names) and app_settings (app display names and icon URLs).
Cloudflare carries the requests and hosts the database, so its own logging and data handling apply to your Worker. See Cloudflare's privacy policy for those.
Who Can Read Your Data
- Recording events needs the API key.
- Reading stats does not. The
GETendpoints under/stats(overview, events, timeline, groups, users and user activity) andGET /aliasesanswer anyone who knows the Worker's address. A user's activity includes their user ID and each event's metadata. - The dashboard contains the API key in its JavaScript, because it is built into the static files.
Treat the Worker address and the dashboard as private: protect the dashboard with Cloudflare Access (see Installation), and don't publish the Worker URL or the key in public code. An API key built into an app can be extracted from the app, so expect that anyone determined can send events to your Worker.
What the Dashboard Sends
The dashboard talks to your Worker. Once a day it also asks GitHub's API (api.github.com) for the latest Oddlytics release, to show an update notice. App icons load from whatever URLs you set in Customize App.
Its preferences (Pretty Format Event Names, Default Time Range, Compact Mode) and the update check's state are kept in your browser's local storage.
Deleting Data
From the dashboard:
- One event: open a user, then click the trash icon next to the event in Recent Activity.
- Everything for one person: open the user and click Delete User Data. This removes their events from every app.
- Everything for one app: open the app and click Delete App Data.
The same actions are available through the API. To remove all events at once, run SQL against D1 from packages/worker:
npx wrangler d1 execute oddlytics-db --remote --command="DELETE FROM events;"Deleted rows can still be restored from D1's Time Travel history until it expires (7 days on the free plan, 30 days on Workers Paid).
Exporting Data
Export the whole database as SQL:
npx wrangler d1 export oddlytics-db --remote --output=oddlytics-backup.sqlIf You Ship Oddlytics in an App
The identifier for vendor counts as a device identifier, and it lets you link one person's activity across all of your apps that report to the same server. Describe this in your app's privacy policy and in its App Store privacy details.