Privacy
Privacy policy.
Last updated:
This page explains exactly what data the site collects, why, how long we keep it, and what rights you have. Plain English first, lawyer-vetted phrasing later — this is a good-faith first pass written by the operator, not a legal team. If anything here is unclear or seems wrong, see the security page for the disclosure address.
1. Who runs this site
mikrotikfilters.com is run as a solo project by an operator based in Australia. The site is hosted on Cloudflare infrastructure (Workers, Pages, D1, R2, KV). Email goes through Resend. Payments — when you donate or subscribe as a supporter — are processed by Stripe. The legal entity behind the site is, at v1.0 launch, the operator as a sole trader; this may change to a Pty Ltd or fiscal host later, in which case this policy will be updated and the date above will move forward.
2. Jurisdictions
The operator is in Australia, so the Privacy Act 1988 and the Australian Privacy Principles (APPs) apply. Visitors from the European Economic Area or the UK are additionally covered by the GDPR / UK GDPR; the operator does not actively target either region but accepts visitors from both, so we treat GDPR as binding for those visitors. California residents covered by CCPA/CPRA can exercise the same rights described in §8 below.
3. What we collect
Different surfaces collect different data. The list below is exhaustive — if a future feature collects something not listed here, it will be added before the feature ships.
3.1. Server logs (everyone)
Every HTTP request to the site goes through Cloudflare's edge. Cloudflare records the standard server-log fields (IP, User-Agent, request path, response code, timestamp) for its own abuse-prevention + caching purposes. The operator does not run any additional access log. Cloudflare's retention is governed by their privacy policy. We do not log requests ourselves, with three specific exceptions below.
3.2. Pull events (anonymous list pulls)
When a router fetches a list at /api/lists/<slug>.rsc,
we record one row in pull_events with: list id,
response status (ok / rate-limited / error), serve source (KV
/ rebuild / R2 fallback), a hashed IP cohort identifier, a
hashed User-Agent, and a timestamp.
The IP cohort hash is critical. It is the SHA-256 of (a per-deploy salt, the IP truncated to /24 for IPv4 or /48 for IPv6). The salt rotates on deploy, and the truncation happens before hashing, so:
- We never store your individual IP — only an opaque identifier that buckets you with the rest of your /24 subnet (typically dozens to thousands of devices).
- We cannot reverse the hash to recover even the cohort — it is one-way, salted, and the salt rotates.
- The hash exists so the operator can spot abuse patterns ("this cohort hammers the API") without identifying anyone in particular.
Pull-event rows are pruned after 30 days by
a daily maintenance cron. The pruner is in
apps/api/src/lists/pullEvents.ts and runs
automatically — there is no human in the loop.
3.3. Authentication
The site uses passwordless magic-link sign-in. To create an account or sign in, you give us your email address. We:
- Store the email address.
- Create a one-time magic-link token (random 32+ bytes, SHA-256 hashed at rest, 30-minute expiry) and email it to you via Resend.
- Once you click the link, we issue a session: a random session id stored only in your browser as an HttpOnly + SameSite cookie; we keep its SHA-256 hash + your role + timestamps in our database. We do not see the raw cookie value.
- We hash your IP and User-Agent (different namespace from the pull-events hash, separate salt) and store those hashes alongside the session row, for the "active sessions" view on /account so you can spot suspicious devices and revoke them.
Sessions expire after a configurable period (default 30 days from last activity). Logging out marks the row as revoked — the row stays for audit; future requests with that session value get rejected.
3.4. Submissions to community lists
If you submit an IP/CIDR to a community list, we store the address, your account id (for moderator visibility), the severity / classification you chose, your free-form comment and evidence URL, and the moderation outcome. Submissions are visible to moderators; approved submissions become public list entries.
3.5. Donations + supporter subscriptions
Stripe handles all payment processing. We never see card numbers or other payment-instrument data — Stripe sends us a webhook with metadata only. We store:
- Stripe customer id + subscription id (or one-time payment id) — opaque references, not card data.
- The amount, currency, and timestamps.
- Linkage to your account when you were signed in at checkout time.
- Optional display name if you opted in to the donor wall (see §3.7 below).
Stripe's own privacy policy covers what they do with the card data on their end. We do not pass any of our other personal data to Stripe beyond what's needed for the Checkout session (your email, when signed in).
3.6. Aggregates (privacy-by-construction)
The numbers shown publicly on
/transparency (monthly support
total, supporter count, etc.) come from a separate
public_aggregates table that is rebuilt
hourly by a cron job. The aggregator:
- Drops any month with fewer than 5 contributors (the small-cohort suppression threshold).
- Bucket-rounds amounts into ranges (e.g. "A$10–A$25") and counts.
- Bucket-rounds time into "this week / this month" without per-event timestamps.
- Never joins to private payment data at read time — the public table is structurally separate.
The result: the transparency page can display "we got 47 supporters this month" without ever revealing how much any one person gave or when.
3.7. Donor wall (opt-in only)
If you opt in by typing a display name on /supporter (one-time donors) or /account (recurring supporters), that name goes into a moderation queue. After a moderator approves it, the name appears on /transparency alphabetically, with no amount, no date, and no cross-reference to your account. To remove your name, edit it on /account or email the operator (see §10) — the next aggregation pass drops the row from the public wall within an hour.
3.8. Submitted scripts (community-scripts library)
If you are a supporter and submit a script to the /scripts library, we store the script body, your title and description, the metadata you chose (category, risk level, RouterOS-version applicability), and your account id as the owner. After moderator approval the script becomes publicly readable + pullable. If you cancel your supporter subscription, your scripts get transferred to a system "community" account 30 days later — they stay live, but ownership is reattributed.
3.9. Audit log
Every state-changing action by an admin or moderator
(approving a submission, suspending a user, rebuilding a
list) writes an audit_log row. The row carries
the actor's user id, the action name, target type/id, a
hashed IP, a hashed User-Agent, free-form metadata, and a
timestamp. The audit log is retained for at least 90 days
and is admin-readable only.
3.10. Error reports (Sentry)
When the API hits an unexpected error, we send a redacted
report to Sentry. The redaction policy
(apps/api/src/observability/sentryRedaction.ts)
strips: request bodies, query strings, all headers except a
tiny allowlist (Content-Type, User-Agent, Accept), all user
fields except the integer user id, all tags except a
curated low-cardinality set (route name, list slug, etc.),
and any string content matching Stripe id patterns,
currency-amount patterns, email patterns, or magic-link
token patterns. The error message + the stack trace go
through; the route + method + status code go through.
No payment amounts, no email addresses, no IPs,
no Stripe identifiers reach Sentry.
4. What we explicitly do not collect
- No third-party tracking cookies. No Google Analytics, no Facebook pixel, no Plausible, no Mixpanel, no Hotjar, none. The site does not set a single tracking cookie.
- No browser-side telemetry. The CSP
connect-src 'self'directive (v0.83.1) blocks third-party fetches at the browser level — even if a future code change introduced one accidentally, the browser would refuse it. - No FLoC / Topics. The
Permissions-Policy: interest-cohort=()header opts out of Google's cohort-tracking scheme. - No fingerprinting. We hash IPs + UAs instead of correlating them.
5. Sub-processors
We rely on the following providers to run the site. Each is bound by their own contractual privacy obligations to us; their published privacy policies govern what they do with the data we send them.
- Cloudflare — hosting (Workers, Pages, D1, R2, KV), edge cache, abuse-prevention WAF.
- Stripe — payments + subscription billing.
- Resend — transactional email (magic-link sign-in, mod-decision notifications).
- Sentry — error reporting (redacted, see §3.10).
6. Legal bases (GDPR)
For EU/UK visitors, the legal bases under GDPR Art. 6 are:
- Contract (Art. 6(1)(b)) — for delivering the supporter subscription and account features you signed up for.
- Legitimate interest (Art. 6(1)(f)) — for abuse prevention, security logging, and rate-limiting. Your interest in privacy was weighed against this; we believe the hashed-cohort approach in §3.2 minimises the intrusion.
- Consent (Art. 6(1)(a)) — for the donor-wall display-name opt-in. You can withdraw consent at any time per §3.7.
- Legal obligation (Art. 6(1)(c)) — when we have to retain payment records for tax-law purposes (Australian Taxation Office requires 5 years of transaction records for GST-registered businesses).
7. Retention periods
| Data | Retention |
|---|---|
| Pull events (cohort hashes + status) | 30 days, then auto-pruned |
| Magic-link tokens | 30 minutes (expiry); rows auto-cleaned |
| Sessions (revoked) | Indefinite for audit; no live use |
| Sessions (active) | 30 days from last activity, then expire |
| Submissions (any status) | Indefinite for audit; you can request deletion (§8) |
| Donations + subscriptions | 5 years (ATO requirement); then auto-pruned where lawful |
| Audit log | Minimum 90 days; longer in practice |
| Error reports (Sentry) | Subject to Sentry's retention; we set 90 days |
| Account email | Until you delete the account, then anonymised |
8. Your rights
You can ask us to:
- Access — give you a copy of all personal data we hold about you, in a portable format.
- Correct — fix any inaccurate data.
- Delete — remove your account and associated data. Some data we have to keep (tax records for 5 years; audit log for the integrity of the platform); we'll explain what stays and why.
- Restrict — pause processing while a complaint is being investigated.
- Object — to processing based on legitimate interest (§6).
- Port — receive your data in a structured machine-readable format you can send elsewhere.
To exercise any of these, email the operator at the address on /security. We aim to respond within 30 days; AU/EU/UK regulators allow up to 30 days under their respective laws and we don't promise faster than the statutory window. A written DSAR runbook is queued before v1.0; once published, it will document the operator's internal procedure.
9. International data transfers
Cloudflare's edge serves your request from the closest of their global PoPs. Stripe processes payments in the United States. Sentry's infrastructure is in the United States. EU visitors' data therefore crosses borders; we rely on Cloudflare's, Stripe's, and Sentry's Standard Contractual Clauses (or successor mechanism) for those transfers. Data minimisation via the redaction + hashing policies in §3 limits what crosses regardless of mechanism.
10. Changes to this policy
Material changes (new sub-processor, new data type collected, retention extended) will be flagged at the top of this page for at least 30 days, and the date above moves forward on every change. Editorial fixes (typos, clarifications) update the date but don't get a banner.
11. Contact
Privacy questions, DSAR requests, or anything that doesn't fit elsewhere: see /security for the operator's email. Australian residents who aren't satisfied with the response can contact the OAIC; EU/UK residents can contact their local data-protection authority.