Privacy Policy
Version 2026-08-15 · Effective August 15, 2026
Streakhaus is a health-and-habits league app. You set goals — steps, sleep, exercise, reading, or anything you can count — and you compete with friends in leagues you choose to join. To do that, Streakhaus reads health data from your phone. This policy explains exactly what it reads, what it derives from it, who else can see what, and how you delete it.
This is not legal advice. It was written to describe what the software actually does, claim by claim, and it should be reviewed by qualified counsel before you rely on it. If anything here does not match the product's behaviour, the product's behaviour is the bug — please tell us at hello@streakhaus.com.
1. Who is responsible for your data
For the hosted service at streakhaus.com, the data controller is:
Stefan Sathianathen
United States
Streakhaus is operated by an individual rather than a company, so there is no registered company name or company number to give.
Contact for any privacy question, access request, or deletion request: hello@streakhaus.com. Requests are answered from that address by the person named above.
Streakhaus is also self-hostable. If you are using an instance somebody else runs, that operator is the controller for your data, this document describes the software rather than their practices, and they may have added to or replaced it. The rest of this policy describes what the software does; it is accurate for any deployment running unmodified Streakhaus.
2. Health data we read from your phone
The Streakhaus mobile app reads health data from your device's health store. It requests read access only, to a deliberately short list, and it asks for nothing else.
On Android, via Health Connect — three record types, read-only:
StepsExerciseSessionSleepSession
On iOS, via HealthKit — three sample types, read-only:
HKQuantityTypeIdentifierStepCountHKQuantityTypeIdentifierAppleExerciseTimeHKCategoryTypeIdentifierSleepAnalysis
That is the whole list. Streakhaus requests no other health data type, no location data, no heart rate, no body measurements, no clinical records, and no background health permission beyond what is needed to refresh the day's totals.
Streakhaus never writes to Apple Health or Health Connect. The app requests
no write, share, or update permission on either platform. On Android it declares
only READ_STEPS, READ_EXERCISE and READ_SLEEP; on iOS it requests a
read-only authorization and no share authorization. Nothing Streakhaus computes
is written back to your health store.
Permission is granular and optional. If you grant steps and refuse sleep, Streakhaus syncs steps and simply has no sleep data — a refused metric is absent, never a zero, and never an error. You can change or revoke these permissions at any time in Health Connect (Android) or the Health app (iOS). Revoking stops all future reads.
What we do with it
For each metric you have a goal on, Streakhaus reads the platform's own daily aggregate for a day — not a stream of raw readings. Both platforms already de-duplicate overlapping sources (a watch and a phone counting the same walk), and Streakhaus takes the platform's single answer rather than re-adding numbers itself.
Steps and exercise leave your phone as one number per day per metric.
Sleep is different, and this is the part worth reading carefully. Your device's health store holds a night as a scatter of fragments — a stretch of light sleep here, an awake minute there, sometimes from two devices at once. Those raw fragments never leave your handset. The app assembles them into a night on the phone and uploads the night. What travels with it is more than the total:
- the time you fell asleep and the time you woke, as instants;
- how much of the noon-to-noon window you were asleep for in total, and how many separate sleep periods there were — which is how a nap is told apart from the night;
- a stage breakdown of the main sleep period — light, deep, REM, unspecified and awake minutes — whenever the source that recorded the night staged it. A phone-only night has no breakdown and none is invented.
We also record which app produced the number, and the name of the device it
came from. Where more than one health app offered a reading, the phone picks a
winner and uploads the winner's identity and the identity of everyone else who
offered — as the app's own package or bundle id, so com.fitbit.FitbitMobile,
com.oura.oura, com.whoop.android,
com.garmin.android.apps.connectmobile, com.sec.android.app.shealth and so
on. Over time that is a picture of which wearables and health apps you use,
and you should know it is being kept. It is kept for one reason: when a number is
disputed — "that is not what my watch says" — the answer has to be a stored fact
rather than an argument.
It is user-private: no other member ever sees it (§3). And it is not an inventory of your phone. Streakhaus never enumerates your installed apps and asks for no permission that would let it; it only learns which app wrote a record it was already permitted to read.
The first sync imports about three months of history. When you first grant health access, Streakhaus does not start from today. It walks back roughly 90 days through your step, exercise and sleep history in two-week chunks, so your streaks, personal bests and progress charts are real on day one rather than empty for a season. The import announces itself in the app while it runs and tells you what it found. It resumes on later launches if it did not finish. This is the single largest read Streakhaus ever performs, and it happens once, off the back of the health permission you granted — there is no separate prompt for it. If you would rather it did not happen, do not grant health access; revoking access stops it mid-way.
Numbers you type in yourself are handled the same way as synced ones, and are limited to today or yesterday.
From those daily values Streakhaus derives:
- whether you met your own goals that day,
- your streaks, and
- your standing in the leagues you have joined.
That is the entire purpose. Health data is not used for advertising, not used to build a profile of you, not sold, and not shared with data brokers.
3. Who can see what — the split that matters
This is the most important section of this policy, and it is enforced in code rather than by convention.
Raw samples are yours alone. The tables holding what your phone reported
(metric_samples), your manually typed entries (manual_events), your sync
bookkeeping (sync_state) and your proof photos (entry_photos) are
user-private. Every single query against them is filtered to the member
making the request, and no function that reads them will accept somebody else's
user id as a parameter — so there is no API filter, page prop or job payload
that could be pointed at another member's raw data. This is enforced by service
code and by isolation tests, and it is checked against staff sessions,
impersonated sessions, and organisation API keys, all three of which are refused
raw health data outright.
Derived daily values are visible to the leagues you joined. Once a day's
data is reduced to a single number per metric per day — your step total for a
day, your sleep minutes, whether a goal was met — that derived value
(daily_metrics) is visible to the other members of a league you have joined.
That is the product: a league you joined exists so that its members can see how
each other did. It is scoped to the league — members of a league you are not in
see nothing. It is scoped further than that: another member sees a metric of
yours on a day only where you held a league-visible goal on that metric, live
that day. A metric nobody in the league is holding you to is not shown, because
the mere presence of a number is itself a disclosure.
Alongside the number they see which platform it came from — "Apple Health", "Health Connect", or "Manual" if you typed it. They do not see which wearable or health app won the day, the name of your device, your sleep-stage breakdown, your sleep and wake times, or anything else described in §2 as travelling with a night. All of that lives on the raw sample, and raw samples are own-eyes-only, for ever.
So: fellow members of a league you joined see the day's totals and whether you met your goals. Nobody sees your underlying samples, and nobody outside that league sees anything at all.
Private goals are visible to nobody. Every goal carries a visibility, set
when you create it and immutable afterwards. A goal marked private never
counts toward the league, never appears in any league listing, standings table,
scores table, streaks table, feed event, recap, public API response, or
commissioner data export, and produces no audit record another member could
read. Asking for another member's private goal by id returns "not found" rather
than "forbidden", so its existence is not disclosed either.
Your email address is not one of them
A league sees names, never email addresses. Nobody in your league — not another member, and not the commissioner — is shown the address you signed up with, on the web or in the app. It signs you in and it carries the messages you have asked for, and that is all it is used for here. (Until 15 August 2026 the web members page did list it to everyone in the league. That was a defect, it is fixed, and this paragraph is now the behaviour rather than a warning.)
Two addresses are still readable by somebody else, and both are addresses that person typed or already held: a pending invitation shows the address it was sent to, to a league's owner and admins only — the people who sent it — and it goes when the invitation is accepted; and our own support staff can look an account up by address when they are helping with it, under the controls in §9.
Two more things members of your league can see
These are not health data, and they are easy to be surprised by, so they are stated rather than left implied.
- A commissioner's "who might want a message" panel. A league's owner and admins have a private screen that points out members whose participation has changed — someone who has gone quiet, whose streak just ended, who is slipping, or who never started. It is built only from things the league can already see (never from your raw samples, never from private goals), it exists so that a human sends a friendly message rather than nobody noticing, and you are never told when you appear on it. Nothing about it is sent to you, and it never appears in a feed, an email, or a push.
- What a stranger sees before joining. A public league is listed at
/leagueswith its name, its description, its timezone and a member count, and anyone can read that without an account. No member's name, email or numbers appear in that listing — the directory cannot return a member identity at all. A private league is not listed and cannot be fetched by anyone outside it.
4. Photos
Attaching a photo to an entry is optional; a league can also turn proof off entirely, in which case no photo is served to anyone, including you.
When you do attach one:
- it is visible only to members of the league you attached it in — a photo never travels to another league, even though the entry behind it may count in several;
- it is visible to them only if that league holds you to a league-visible goal on that metric on the day the entry counts. A photo attached to a goal only you can see stays visible only to you, everywhere;
- you can delete it at any time from your log screen, which removes the record and the image file;
- image files are re-encoded on upload and EXIF metadata is stripped, so the location and device data cameras embed in a photograph does not reach our disk or another member's screen. The file you picked is sent to our server as you picked it and re-encoded there; only the re-encoded copies are ever written to disk or served. The re-encoded copy is then re-read and refused outright if any EXIF, XMP or IPTC block survived, so "we stripped it" is checked rather than assumed.
The same is true of your profile picture, which is visible to every member of every league you are in.
5. Other information we handle
- Account data — your name, email address, and authentication metadata (password hash, two-factor secret if you enable it, active sessions and the devices they were created on). Also a record of which version of these policies you accepted, and when.
- League data — the leagues you belong to, your per-league nickname, your goals, your scores, streaks, rounds, feed posts and comments.
- Sign-in metadata — each web session stores the IP address and browser user-agent it was created from. You can see your own in your account settings; we email them to you when a sign-in comes from a device we have not seen before, so that a session you did not start is visible to you. The mobile app's requests do not create these rows.
- Push tokens — if you enable push notifications: the notification token your device was issued, its platform, an install identifier the app generates for itself (not an advertising id — there is no advertising id anywhere in this product), and the device's name. Phones are usually named after their owner, so this field commonly contains a real first name — "Stefan's iPhone". It is stored so that you can tell your own devices apart when revoking one.
- Your notification history — the title and body of every notification we have sent you, the link it pointed at, and whether and when you opened it. This is what fills the in-app bell. Your per-type, per-channel notification preferences are stored alongside it.
- Invitations you send — the email address of anyone you invite to a league, until the invitation is accepted or expires. Streakhaus never reads your address book; there is no contacts permission in the app at all. An invited address is one you typed.
- Product-usage records — first-party counters of actions taken in a league ("a goal was created", "a day was logged"), recorded against the member who took them, together with an audit trail of league administration. These stay in our own database; nothing ships them to anybody.
- Operational data — server logs, job records and security metadata used to run the service, detect abuse and diagnose failures. See §8 for what is actually in them.
6. Third parties
Streakhaus deliberately has very few. There is no advertising, there are no advertising identifiers, and there is no third-party analytics or product-tracking SDK in either the web app or the mobile app. The web app's Content-Security-Policy permits no external hosts at all — fonts and assets are served from the service's own origin. Usage counting, where it exists, is first-party and stays in our own database. We do not sell your personal information, and we do not share health data with third parties for their own purposes.
The services that do handle data on our behalf:
- Email delivery. Account email — sign-in links, verification, notification digests — is sent through an SMTP provider configured by the operator of the instance. That provider sees the recipient address, the display names, league names and notification text in the message, and — in the alert we send when a sign-in comes from a new device — the sign-in's IP address and browser user-agent, because telling you about a sign-in you did not make means telling you where it came from.
- Push notifications. If you enable them, notifications are delivered through Expo's push notification service, which in turn hands them to Apple (APNs) or Google (FCM) for delivery to your device. A notification carries its display text and a link to the screen it refers to. That text can contain goal titles, streak lengths, a league rank, another member's display name, a league-wide shared goal total — and a figure derived from your own day or week, such as your weekly step total or how many steps separate you from the member ahead of you. It never contains your raw health samples, your sleep stages, your sleep and wake times, or the name of the app or device a number came from. Android notifications are set to hide their content on the lock screen. If you would rather none of this leaves the app, turn push off; the same information is still available in-app.
- Strava, only if you choose to connect it — see below.
- Error diagnostics, only if the operator has configured them — see §8.
There is no payment processor, because Streakhaus has no paid plans. No payment or card details are collected.
7. Strava (optional)
Connecting Strava is entirely optional and Streakhaus works fully without it.
If you connect it, Streakhaus requests the narrow activity:read scope — not
activity:read_all, not your private activities, not your profile, and not
write access — and uses it to show your own recent activity on your own
day screen, as context beside your numbers.
Three things are true about it and are enforced in code:
- No other member ever sees it. The read is refused unless the person asking is the person who connected the account; another member asking gets nothing back, and cannot even learn whether you use Strava. Staff sessions, impersonated sessions and API keys are refused as well.
- It is never used for scoring. Strava is not a metric source. It never writes a metric sample, a daily metric, a score, a streak or a feed event, and it plays no part in whether you met a goal or where you rank.
- Disconnecting purges it. Disconnecting deletes the stored authorisation and every cached activity of yours, and sends a deauthorisation to Strava. Deleting your account does the same thing, immediately, before anything else.
Cached Strava activity holds a small allowlist of fields — the activity's id, its name, its sport type, distance, moving time, elevation gain, start date and average speed — and no location data of any kind. No GPS track, no start point, no map: those fields are never even constructed from Strava's reply, so there is no code path that could store one. Nothing about you is sent to Strava beyond your own authorisation.
If the operator of your instance has not configured Strava credentials, the feature does not exist at all — every route returns "not found".
8. Security, staff access, and where it stops
Leagues are isolated from one another at several layers, including Postgres row-level security, and the application connects to the database as a least-privilege role that cannot bypass it.
The user-private tables described in §3 are the honest exception: they carry no league column, so their privacy is enforced by service code and isolation tests rather than by the database. That is a deliberate design decision, the enforcing code is deliberately small enough to audit in one sitting, and the tests exist specifically to try to break it — but it is a different kind of guarantee from the database-level one, and you should know which is which.
What our support staff can see
Streakhaus has an internal staff console. It is served only on a separate hostname, requires a staff role that cannot be granted from inside the product, and requires two-factor authentication. From it, staff can see: your name, email address, whether your email is verified, when you signed up, which leagues you belong to and in what role, and your active sessions with their IP addresses and browser user-agents. They can see member-authored text that has been reported, and league-level counts.
Staff cannot see your health data. Not your samples, not your daily values, not your scores or streaks — the console does not read those tables, and the code that guards raw health data refuses a staff session outright, in the same breath as it refuses an impersonated session and an API key. The same refusal covers deleting your health data: support staff can neither read it nor delete it, even if you ask them to. You do it yourself (§9).
Staff can sign in as you, and it is recorded. Support can open a time-limited impersonation session — for a bounded period, never against another staff account, and never without typing a written reason, which is stored. While impersonating, they see the product as you see it, but they still cannot reach your raw health data, cannot change your credentials, cannot reveal an API key secret and cannot see a commissioner's care panel. We do not currently notify you when this happens. If that matters to you, ask us and we will tell you whether it ever has.
The honest limit on all of the above: these are boundaries enforced by our code, not by the database. Whoever operates the server has direct access to the database and could read anything in it. On the hosted service that is the one person named in §1.
What is in our server logs
Server logs are written to the machine's own disk and shipped nowhere. They are not redacted, and you should assume they contain email addresses (when we send you an email) and internal user ids (routinely). They do not contain IP addresses, and they do not contain request or response bodies — so no health value, no photo and no message text reaches a log line. Background jobs briefly hold the things they are about to send: an email's recipient address, a push message's text and token. The two queues that carry those are cleared an hour after the job runs.
Error diagnostics. The software can report crashes and errors to an error-tracking service, but this is off unless the operator configures it, and the software does not perform field-level scrubbing of error reports. Where it is enabled, an error report could in principle include data from the request that failed. If you are self-hosting, this is your decision to make; for the hosted service, ask us at hello@streakhaus.com.
No system is perfectly secure. Isolation between leagues is treated as a guarantee rather than a convention, and we would rather tell you where the boundaries are than imply there are none.
9. Deleting your data
Deleting a photo. Remove any proof photo from your log screen at any time. The record and the image file both go.
Deleting a day's manual entries. Entries you typed yourself can be cleared for a given day and metric. This removes only your own entries; it does not remove a value that came from your watch or phone.
Disconnecting Strava. Deletes the authorisation and all cached Strava activity immediately, and tells Strava.
Revoking health permissions. Revoking Streakhaus's access in Health Connect or the Health app stops all future reads immediately. On its own it does not remove data already synced — that is the control described below, and the two are meant to be used together.
Deleting your account. From your account settings on the web, or from the app on your phone, after re-authenticating. Your account is deactivated immediately; every credential it holds is revoked — browser sessions and the long-lived token each of your phones holds, which is the one people forget — and your Strava connection is disconnected locally and a deauthorisation is sent to Strava. Sign-in is refused from that moment.
And we stop contacting you at that moment too. Every device you had registered for push notifications is unregistered as part of the deletion, and from then on nothing is sent to your account on any channel — no push, no email, no notification of any kind, including your league's weekly recap. You do not have to turn notifications off first. One honest caveat remains: a browser you already have open may keep working for up to five minutes.
The account and everything keyed to it — health samples, manual entries, sync state, daily metrics, goals, photos and their image files, notification history and push tokens — are then permanently purged 30 days after the deletion request. The 30-day window exists so that an accidental or coerced deletion can be reversed by contacting us.
Two things outlive that purge, and we would rather say so than let you find out:
- Administrative and audit records — a line saying that an action was taken in a league, and the internal id of the account that took it — are kept, so that a league's own history and our record of staff access stay intact. They contain no health data.
- Encrypted database backups, which by default retain daily, weekly and monthly snapshots going back several months. A purged account can still be present in a snapshot taken before the purge. We do not mine backups and we do not use them to restore a purged account; they age out on their own schedule and are then gone.
Deleting synced health data without deleting your account
You can delete everything Streakhaus has synced from Health Connect or the Health app without deleting your account, and without asking us. It takes effect immediately — there is no 30-day window, because nothing is being deactivated, only deleted.
Where the control is. In the mobile app: Settings → Delete synced health data, directly beneath the card describing what the app is allowed to read. On the web: Account → Health data, which first shows you how much is stored and over what date range, then offers to delete it.
What it deletes, everywhere and with no exception:
- Every health reading synced to your account (
metric_samples) — every day, every metric, every phone, however old. No date window is kept back. - Your sync bookkeeping (
sync_state) — the incremental cursor each phone resumes from, the stored "last synced" time and the last error. - Your history-import record (
health_backfill) — how far back through your own history the one-time import set out to reach, how far through it got, and how many readings it wrote. It holds no health value, but it describes the shape of the history that was imported, so it goes with it. A phone that imports again starts from the beginning, because the record of what was already imported is gone. - The daily values those readings produced (
daily_metrics) in every league you compete in, so your leagues stop seeing numbers whose source no longer exists — with the one exception below.
What it does not delete, and why:
- Weeks your league has already published stay. A round that has been finalised is a published result about a league rather than a private record: its standings, its recap and its scores were sent to everyone in it. One member's deletion does not rewrite history everyone has already seen, so the daily values inside an already-finalised week remain, and every day since the last finalised week is recalculated without your synced data at once. If you want those removed as well, delete your account — the 30-day purge takes everything, published weeks included.
- Numbers you typed in yourself stay. Manual entries are not synced health data; they have their own control (above), which clears them a day and a metric at a time.
- Future syncing is not switched off. Revoking Streakhaus's access in Health Connect or the Health app is what stops your phone reading new data. If you delete without revoking, your phone will sync fresh data again — and because the cursor is gone too, it may re-read from your platform's own history. To delete permanently, revoke access first, then delete.
Only you can do this. The deletion runs against the account making the request and cannot be pointed at anyone else. As with reading raw health data, it is refused outright to staff sessions, impersonated sessions and organisation API keys — support staff can neither read your health data nor delete it.
10. Your rights
Depending on where you live, you may have the right to access, correct, export, or delete your personal data, to object to or restrict processing, and to complain to your local data-protection authority. Exercise any of these by writing to hello@streakhaus.com; we will respond within the period the applicable law requires.
Two of these you can exercise yourself, without writing to anybody: deleting your synced health data (§9) and deleting your account (§9). Both take effect when you press the button.
A league's commissioner can export their league's data, which contains the league-shared values described in §3 and excludes private goals and photos. A self-serve export of your own personal data is not yet available — ask us and we will produce one.
11. Children
Streakhaus is not directed at children and is not intended for use by anyone under 16. We do not knowingly collect health data from children. If you believe a child has created an account, contact us and we will delete it.
12. Changes to this policy
We may update this policy. When we make a material change we will update the version above and ask you to re-accept the current version the next time you sign in.
13. Contact
Privacy questions, access requests, and deletion requests: hello@streakhaus.com. Email is the only channel — Streakhaus has no contact form. Two things you never have to ask us for: your synced health data and your account can both be deleted by you, from the app, under Settings (§9).