Published revision 1.0 (2026-10-18). This page is generated from the canonical policy text; both carry the same revision.
TameFlash data and privacy policy
Revision 1.0 — 2026-10-18. This file is the canonical, owner-approved text (issue #109). The published copy at https://tameflash.com/privacy (Cloudflare Pages) is generated from this file by scripts/build-site.mjs and carries the same revision. The extension bundles the revision its Privacy link points at and shows a one-time notice in the popup and on the Options page when that revision changes (a fresh install records the current revision at install time, so only a change is announced).
Data controller: Jan Bednář, an individual developer in the European Union. Privacy requests, deletion requests and support: [email protected]. GDPR applies to users in the EU and is the baseline applied to everyone.
1. Summary
- TameFlash analyses video on your device. Nothing about what you watch leaves the browser unless you deliberately submit a report.
- The only requests TameFlash sends to its own service today are the manual incident report (you press "Report a problem", review the exact payload, and press Submit; each report is a separate decision) and the deletion of such a report (you press Delete next to it; the request carries that report's deletion code). (Your browser may additionally synchronise the extension's settings between your own profiles through its vendor's sync service; TameFlash never receives that data, see section 4.1.)
- A report never contains the page URL, the video title, your account, your IP address as a stored field, or any identifier of your installation. It contains the public video identifier of the supported provider when one exists, the playback time, the protection state at that moment, the detector's recent hazard summary, and an optional note you typed.
- Reports are stored by Cloudflare (Workers, D1) in a database created with Cloudflare's EU jurisdiction constraint. A report becomes eligible for deletion 180 days after receipt (reports received before 2026-10-19 keep the 90-day schedule they were submitted under), a note after 30 days; an hourly cleanup job removes eligible rows, so actual removal follows that schedule and a backlog or outage can delay it. You can delete a report earlier with the one-time deletion code kept on your device: that removes it from the active database at once; Cloudflare's point-in-time backup history can still contain it for up to 30 days before it expires.
- The optional note is free text. You are told not to type health details, names or contact information, but the service cannot prevent it, so a note may contain personal data you chose to type; it is read by the maintainer only and deleted on sight when it does.
- No data is sold, shared with third parties, or used for advertising.
- The service is not anonymous: omitting accounts and install identifiers does not stop Cloudflare from handling the ordinary network metadata (IP address, request time) that any HTTPS request carries. Section 4 says what happens to it.
- Every planned feature that would send more (exact video lookups, automatic evidence sharing, creator uploads) is off, unbuilt, and marked PROPOSED below. Each needs a separate owner approval on its issue before it ships.
2. Locked constraints (from issue #109, verbatim in substance)
- Manual submission is per-report consent; any optional diagnostics bundle requires a separate unchecked opt-in.
- Exact catalog lookups and automatic detector-evidence sharing are independent, default-off controls.
- No stable installation identifier is introduced merely to rate-limit or establish crowd quorum.
- No production endpoint, automatic lookup, evidence upload, creator upload, or catalog publication ships until this policy is accepted (for the shipped class, it is; for the planned classes, their PROPOSED rows must be approved first).
- Automated downloading or retrieval from YouTube is out of scope unless a later authorization review explicitly permits it. The first scanner accepts authorized uploaded files only.
- The policy must never describe the service as anonymous merely because it omits an account or installation ID.
3. Decisions (owner, 2026-10-18) and PROPOSED values
| Item | Status | Value |
|---|---|---|
| Controller and law | DECIDED | Jan Bednář, individual, EU; GDPR baseline for all users |
| Contact route | DECIDED | Email only: [email protected] (the repository may stay private) |
| Manual-report retention | DECIDED | Report body eligible for deletion 180 days after receipt (reports received before 2026-10-19 keep their 90-day schedule: longer retention is not applied retroactively); note 30 days; removal by the hourly cleanup job; after deletion or expiry only a hashed idempotency tombstone for 7 days |
| Free-text notes | DECIDED | Read by the maintainer only (Cloudflare dashboard / D1 console); a note that contains health details, names or contact data is deleted as soon as it is seen; never shared or exported |
| Publication | DECIDED | This file is canonical; https://tameflash.com/privacy on Cloudflare Pages mirrors the approved revision before the store listing goes live (#5) |
| Change notification | DECIDED | Versioned policy; one-time in-extension notice when the bundled revision changes (src/core/policyRevision.ts; acknowledged revision stored device-locally under tameflashAcknowledgedPolicyRevision); a revision that collects more or keeps data longer is marked stricter, announced with its effective date at least 30 days after publication, and every consent-based feature stays off until re-enabled |
| Incident response | DECIDED | Contain (disable the endpoint, rotate Cloudflare credentials, delete affected rows), assess, record; notify users through an in-extension notice and the website, and the supervisory authority within 72 hours where GDPR requires it |
| Approval scope | DECIDED | Shipped flows (local processing, manual reports) are approved now; planned flows below are PROPOSED and each later issue must obtain approval on #109 or its own issue before shipping |
| Exact lookup data | PROPOSED | Request body only (canonical video identity + compatibility metadata); no server-side storage of lookups beyond Cloudflare's sampled operational logs; cache on device ≤ 7 days |
| Detector-evidence sharing | PROPOSED | Candidate evidence kept 365 days while under review; rejected evidence deleted 30 days after rejection; verified intervals kept while the catalog record is live, derived and withdrawable |
| Creator uploads (preflight) | PROPOSED | Source media deleted from R2 within 7 days after the scan completes or fails; scan results kept 90 days unless the creator deletes them earlier or publishes them; creator tokens hashed, revocable, kept until revoked |
| Exceptional retention | PROPOSED | Material tied to an abuse or security investigation may be kept up to 90 days beyond its schedule, with a written reason in the incident record |
| Scanner job records | PROPOSED | Job metadata (digests, build, revision, throughput) kept 365 days; scratch data deleted after each job |
4. Data inventory
Columns: what is collected; purpose; lawful basis; where; who can access; retention; deletion path; backups and logs; incident handling. "Device" means the user's browser profile. Most Device rows never leave it; the exceptions are stated in the row itself: a report draft's serialized payload, note and idempotency key are transmitted when the user submits or retries that report, and a stored deletion code is transmitted when the user deletes that report. Nothing in a Device row is transmitted without one of those explicit actions.
4.1 Shipped today
| Class | Fields | Purpose | Basis | Storage | Access | Retention | Deletion | Backups / logs | Incident handling |
|---|---|---|---|---|---|---|---|---|---|
| Local video analysis | Decoded frames, luminance statistics, hazard risk per frame, lookahead buffers | Flash reduction | Not personal data processing by the controller: happens entirely on the device | Device memory only | The page's own tab | Discarded continuously; nothing persisted | n/a | None | n/a |
| Settings (synced) | The SETTINGS_SCHEMA keys: enabled, safe mode, strength, sensitivity, startup/degradation policies, detector thresholds, HDR display nits, WebGPU toggles, debug HUD | Keep the user's preferences across their own browser profiles | User's own browser sync; TameFlash never receives it | chrome.storage.sync (the browser vendor syncs it under its own policy) | The user | Until changed or the extension is removed | Change or uninstall | Browser vendor's sync backups, outside TameFlash | n/a |
| Device-local state | Site protection policy per origin (tameflashOriginPolicy: the exact origins of websites the user added, with their mode), the acknowledged privacy-policy revision (tameflashAcknowledgedPolicyRevision), expert/diagnostic preferences, compatibility mismatch flag, safety diagnostics (event list: protection states, failure reasons, settings keys, timings, plus the browser's user-agent string stored once per recorder for version context; events about an emergency cover carry that cover's random session id, which therefore outlives the cover in the diagnostics until eviction or Options → Clear; no URLs, no credentials; bounded per recorder, see below), report drafts (tameflashIncidentReportDraft:*: the complete captured report context, and once a submission was attempted also the serialized payload including the optional note, the idempotency key and the plaintext deletion secret, kept until the draft's 30-minute sweep so a timed-out or offline submission can be retried without changing the payload), submitted-report receipts with their deletion codes (tameflashSubmittedIncidentReport:*), generic-site activation records (session storage) | Operate the extension; let the user delete reports later | Local operation | chrome.storage.local / session | The user; the extension itself | Drafts become eligible for removal 30 minutes after capture and are removed by a housekeeping check that runs every 5 minutes while the browser is running (so up to about 35 minutes, longer if the browser is closed meanwhile); diagnostics are bounded per recorder (content script, bootstrap, service worker: 100 events / 20 errors each, so at most 300 events / 60 errors on the device; the Options view shows the merged, trimmed list) and cleared from Options; session storage ends with the browser session; a receipt and its deletion code stay on the device until the user presses Delete for that report (also after the server has already expired it, which answers "not found" and removes the local record) or uninstalls the extension | Options → Clear (diagnostics), the report page's Delete button (receipts), uninstall | None (local only) | n/a |
| Emergency-cover session state | While "Pause and cover" is active: the covered tab id, a random session id, the activation time, sometimes the window id and tab index (activePanicSessions), and a ledger of random cover tokens per covered tab (activeUniversalPanicCoverTokens) that proves which cover the extension owns | Keep the emergency cover authoritative across worker restarts and tab moves | Local operation | chrome.storage.session | The extension itself | Until the cover is released or the browser session ends; session storage never survives a browser restart (the random session id also appears in safety-diagnostics events, where it stays until eviction or Options → Clear) | Release the cover; close the browser | None (local only); never transmitted | n/a |
| Diagnostic video labels (opt-in) | When the user enables "diagnostic video labels" in Options, each safety-diagnostics event also stores a sanitized label of the video it concerns: the page title on dedicated providers, the hostname on added websites, cut to 160 characters | Let the user match a diagnostic event to the video it happened on | Local operation; the user's explicit opt-in | chrome.storage.local (tameflashSafetyDiagnostics) | The user | With the diagnostics (100 events); turning the preference off stops new labels and existing labels are dropped at the next write; Options → Clear removes them | Options → Clear, disable the preference, uninstall | None (local only); never transmitted, not part of reports | n/a |
| Manual incident report (transmitted) | schemaVersion, kind (flash-through / over-dimmed / protection-unavailable), reportedMediaTimeSec, optional note (≤ 1000 characters), context: capture time (UTC), extension version, canonical media reference (provider, provider id, public video id for YouTube/Twitch/Kick where one exists, content kind, confidence, ad state, catalog eligibility), media session identity (provider route identity; withheld for added websites), video session id (a per-video value made of the capture time and a random suffix, not tied to the user), media time, duration, playback rate, paused, live edge, analysis revision, detector/response/scanner fingerprints (hashes of settings profiles, not of the user), protection state, failure reason, lookahead state, HDR flags, safe mode, weakened keys, mitigation flag, up to 20 recent hazard episodes (times, peak risk, channels, display area), up to 600 trace samples (media time, risk values, applied gain). Never: page URL, title, hostname of added websites, account, cookies, user agent, IP as a field, install identifier | Let the maintainer reproduce a protection failure or over-dimming on a specific public video | Consent, given per report by pressing Submit after reviewing the exact payload | Cloudflare Worker tameflash-reports → D1 database tameflash-reports-eu, created with --jurisdiction=eu (Cloudflare guarantees the data stays in the EU; it runs in the EEUR region) | Maintainer only, via the Cloudflare dashboard / D1 console | Body: eligible for deletion 180 days after receipt; reports received before 2026-10-19 keep the 90-day schedule they were submitted under. Note: eligible after 30 days. An hourly cleanup job removes eligible rows in bounded batches, so removal follows that schedule (a backlog or outage delays it and is monitored as a job failure). Deleted/expired rows leave only a hashed idempotency key for 7 days so a retried submission cannot recreate them | DELETE /v1/reports/{receipt} with the one-time deletion code kept on the device (server stores only its SHA-256); the report page lists submitted reports with a Delete button; email requests honoured for anyone who can quote a receipt. Deletion removes the rows from the active database immediately; Cloudflare's point-in-time history ("Time Travel", always on) can restore the database to a state before the deletion for up to 30 days, after which the data is gone from that history too | Cloudflare D1 Time Travel (up to 30 days) is the only backup and is outside the application schedule; the Worker records only status/error codes and retention counters at 10 % sampling; Workers invocation logs (which would include URLs) are disabled in the checked configuration | Section 6 |
| Request metadata (Cloudflare edge) | Connecting IP address, request time, TLS and HTTP metadata, the request's Origin header (a fixed Chromium extension origin shared by every install; on Firefox a per-install moz-extension:// UUID) | Deliver the request; rate-limit abuse (10 requests per 60-second window per connecting IP, through Cloudflare's Workers rate-limiting binding) | Legitimate interest in running and protecting the service | Cloudflare's global edge network: the Worker runs at the Cloudflare location nearest the user, wherever that is; only the database is EU-constrained (no Regional Services or metadata-boundary product is configured) | Cloudflare as processor; the maintainer sees only sampled application logs without IP or Origin | The rate-limit counter is keyed by the connecting IP and held by Cloudflare's platform (cached on the serving machine and asynchronously stored in that Cloudflare location); the 60-second period is the limiting window, not a deletion deadline, and the application receives no retention guarantee for the key beyond Cloudflare's own terms. The Firefox Origin is matched by shape and never logged or stored; Cloudflare retains edge metadata under its own terms | Not applicable to the application; Cloudflare's retention | See Cloudflare's processor terms; account-level Logpush and Tail Workers are not enabled | Section 6 |
| Operational logs | Bounded application log lines: status code, error code, retention counters | Operate and debug the service | Legitimate interest | Cloudflare Workers Logs | Maintainer | Cloudflare's Workers Logs retention (platform-controlled, days); no payload, note, URL, IP or Origin content | n/a | Platform | Section 6 |
| Deletion receipt | On the device: receipt id + deletion secret. On the server: receipt id, SHA-256 of the secret, hashed idempotency key | Prove the right to delete without any account | Consent (part of the report) | Device chrome.storage.local; D1 | The user; the maintainer sees hashes only | Device: until the user presses Delete for that report or uninstalls; server: with the report, then the 7-day tombstone | Deleting the report (the only local-clear mechanism today; a server-side expiry does not remove the local record by itself) | As above | Section 6 |
4.2 Planned, not built, PROPOSED
| Class | Fields | Purpose | Basis | Storage | Access | Retention (PROPOSED) | Deletion | Notes |
|---|---|---|---|---|---|---|---|---|
| Exact video lookup (#120, #121) | POST body: canonical video identity of a positively identified, catalog-eligible YouTube VOD/Short, analysis-compatibility metadata | Warn before a verified hazardous video | Consent (default-off toggle; pre-enable preview shows the exact body) | Cloudflare Worker; no lookup table | Maintainer (logs only) | No server-side storage of individual lookups; sampled operational logs only; device cache ≤ 7 days | Toggle off; clear device cache | Reveals the watched video to the service at lookup time; disclosed in the toggle copy |
| Detector-evidence sharing (#121, #120) | Canonical identity, analysis revision, detector fingerprint, content-driven hazard episodes, raw detector summaries, non-identifying provenance; one idempotent summary per media session | Build the candidate queue for the verified catalog | Consent (separate default-off toggle) | D1 candidate tables, separated from verified records | Maintainer, reviewers | Candidates 365 days while under review; rejected 30 days after rejection; verified intervals while the catalog record is live, withdrawable | Toggle off stops future uploads; deletion of a specific evidence row on request with its receipt | Live, ad, unknown-identity sessions can never produce evidence |
| Verified catalog records (#120) | Canonical identity, verified intervals, revision, review state, scanner provenance | Serve lookups | Legitimate interest (derived, non-personal) | D1 | Public through lookups | While the record is live; withdrawn records are tombstoned | Withdrawal by the maintainer | Not personal data; listed for completeness |
| Creator accounts/tokens (#124) | Maintainer-issued token (hashed), creator contact for the invitation, quota counters | Invite-only preflight scanning | Contract/consent with the creator | D1 | Maintainer | Until revoked; counters rolling | Revocation | Tokens never double as extension-user identifiers |
| Uploaded media (#124, #114) | The creator's file, digest, media metadata, rights attestation (who, when, which file) | Scan before publication | Consent + rights attestation | Cloudflare R2 (temporary), scanner scratch | Maintainer, scanner | Source media deleted within 7 days after the scan completes or fails; attestation kept with the job record | Creator deletes earlier via the preflight page | Attestation wording in section 7 |
| Scan jobs and results (#114, #124) | Source authorization, digest, scanner build, analysis revision, profile, throughput, verified intervals, diagnostics | Produce verified intervals; operations | Legitimate interest / contract | D1, scanner logs | Maintainer; the creator for their own results | Results 90 days unless deleted or published; job metadata 365 days; scratch deleted after each job | Creator deletion; maintainer | Publication is a separate explicit choice |
| Scanner operational logs (#114) | Job ids, timings, error codes | Operate the scanner | Legitimate interest | Scanner host | Maintainer | 30 days | n/a | Never media bytes or creator contact in logs |
| Privacy ledger (#122) | Device-only list of the latest 100 network operations for ≤ 30 days, with media ids redacted | Transparency | Local operation | Device | The user | 30 days / 100 entries | "Clear local ledger" | Never uploaded |
5. Processor boundaries
Cloudflare (Workers, D1, Workers Logs; R2 once uploads exist) is the only processor today. The Worker runs on Cloudflare's global edge network at the location nearest the user, so the request itself (IP address, timing, TLS metadata, Origin) is handled wherever that location is; no Regional Services or Customer Metadata Boundary product is configured, and this policy does not claim an EU boundary for request handling. The production database tameflash-reports-eu was created on 2026-10-18 with wrangler d1 create --jurisdiction=eu, the only way D1 location can be constrained, so EU residency is a Cloudflare guarantee rather than an observed region (it runs in EEUR). The previous database, created without a constraint and holding no user reports, is retired with the deploy that publishes this revision. Any future R2 bucket is created with the same EU jurisdiction. Cloudflare necessarily sees connecting IP addresses, request timing, TLS metadata and the request Origin. The application does not store any of them itself: the rate limiter is Cloudflare's Workers rate-limiting binding keyed by the connecting IP (counters are cached on the serving machine and asynchronously stored in that Cloudflare location; the 60-second period is the limiting window, not a documented deletion deadline), the Firefox per-install Origin is matched by shape only, Workers invocation logs are disabled in wrangler.toml, and application logs carry status and error codes only. Account-level Logpush, Tail Workers and Cloudflare's own edge analytics are governed by Cloudflare's terms, not by this repository, and are not enabled for this service.
Dedicated scanner (planned, #114): a maintainer-operated host that talks only to the authenticated Worker API and never receives direct database access. It holds uploaded media only for the duration of a job plus the deletion window above. Its trust boundary is the Worker's authentication; media never reaches it from any source other than maintainer fixtures or rights-attested uploads.
6. Incident response
- Contain: disable the affected endpoint (
wranglerroute removal or an immediate deploy of a refusing worker), rotate the Cloudflare API credentials and any scanner tokens, delete rows known to be affected. - Assess: what classes (section 4) and which time window were exposed; record the incident with timestamps in the maintainer's incident log.
- Notify: users through a one-time in-extension notice and the website; the competent supervisory authority within 72 hours of becoming aware where GDPR requires it; affected creators directly when uploads are involved.
- Follow-up: a CHANGELOG entry and, where practices change, a policy revision under the change-notification rule.
7. Provider authorization and rights attestation
- Production scanning accepts only maintainer-owned fixtures or files uploaded by a creator or rights holder with an explicit attestation. No automated downloading or retrieval from YouTube or any other provider, unless a later authorization review explicitly permits it and this policy is revised.
- Attestation wording (PROPOSED), shown unchecked before each upload: "I own this media or have the rights holder's authority to upload it to TameFlash for flash-hazard analysis. I understand the file is deleted within 7 days after the scan and that publishing results to the public catalog is a separate choice."
- Catalog publication after a scan requires a catalog-eligible canonical YouTube identity supplied and validated separately from the uploaded bytes, explicit publication consent, and the ordinary review pipeline.
8. Consent wording and placement
Manual report (shipped). The report page shows "The context below was captured when you clicked the report button. Review everything before submitting — nothing is sent without your explicit submit", the exact serialized payload under "Exactly what will be submitted", and, next to the note field, "Your note is stored on the report server when you submit. Do not include health details, names, or contact information." The Submit button is the consent. Each submitted report keeps its one-time deletion code on the device under "Previously submitted reports" with the explanation "Deleting removes the report and its note from the active database at once; Cloudflare's point-in-time backup history can still contain them for up to 30 days before it expires."
Optional diagnostics bundle. Not implemented. If added, it is a separate unchecked checkbox on the report page with its own field list.
Exact video lookup (PROPOSED, #121). Options toggle, off by default, with: "When on, TameFlash sends the identifier of the YouTube video you are watching to api.tameflash.com to check it against verified hazard records. This reveals what you watch to the service at that moment. Cloudflare still handles your IP address and the time of the request. Nothing is stored about the lookup." The toggle shows the exact request body before enabling.
Automatic detector-evidence sharing (PROPOSED, #121). Separate toggle, off by default: "When on, TameFlash uploads one summary per video of what its detector found (video identifier, hazard intervals, detector settings profile). It never uploads frames, notes, your settings strength, or anything about ads or live streams." The toggle shows an example payload before enabling.
Creator upload (PROPOSED, #124). The attestation in section 7, unchecked, followed by the limits and the deletion schedule, on the preflight page.
9. Your rights and how to exercise them
- Access, rectification, erasure, restriction, objection and portability under GDPR. Because reports carry no account, a request must quote the report receipt shown on the device; without it the controller cannot identify a report as yours, and the automatic schedule applies.
- Deletion: the report page's Delete button (immediate) or [email protected].
- Complaints: your national supervisory authority; for the controller's seat, the Czech Office for Personal Data Protection (ÚOOÚ).
10. Store disclosures (Chrome Web Store, AMO)
Mapping to the Chrome Web Store user-data policy and disclosure requirements, and to AMO's privacy questions:
- Collected data categories (Chrome's definitions apply to on-device processing and storage too): "Website content" (on-device) — decoded frames of the video being protected are read and analysed on the device during every protected playback; they are never transmitted or persisted; "Location" — the connecting IP address reaches Cloudflare whenever a report is submitted or deleted, is used by Cloudflare's rate limiter and edge processing as described in sections 4 and 5, and is never stored by the application; "Authentication information" — a per-report deletion code (a random 64-character secret) is created on the device for each submitted report, stored in
chrome.storage.local, and sent as a bearer credential only when the user deletes that report; it stays until that deletion or uninstall and authenticates nothing else; "Web history" — the exact origins of websites the user adds to protection are stored on the device (tameflashOriginPolicy, never transmitted), the opt-in diagnostic video labels store page titles or hostnames of videos the extension protected (device only, cleared with the diagnostics), and a manual report identifies the specific public video it is about; "Website content" (transmitted: the public video identifier and playback position of supported providers) and "User activity" (the detector's hazard summary) — only within a manual report, only on explicit submit. Nothing else is transmitted. - May be collected only inside the optional free-text note, because the service cannot enforce what a user types despite the warning: personally identifiable information, health information, personal communications. The store disclosure therefore lists these as "collected, only if the user types them into the optional note; read by the maintainer only; deleted on sight; never used for any other purpose". Not collected in any form: financial information, account credentials of any kind, personal communications outside the optional note.
- Certifications: data is not sold; not used for purposes unrelated to the extension's single purpose (flash reduction and reporting its failures); not used for creditworthiness or lending.
- Remote code: none; dynamically registered scripts are packaged files (see
docs/STORE_REVIEW_NOTES.md). - Privacy policy URL:
https://tameflash.com/privacy(Cloudflare Pages), showing this revision. - Permissions justification lives in
docs/STORE_REVIEW_NOTES.md; the optional all-sites host permission is requested only from a user click and governed by the device-local site policy. - Diff against the #5 checklist assumptions: the checklist already states "manual incident reports are sent to api.tameflash.com only after the user reviews the exact payload and presses Submit"; this policy adds the 180-day figure (the checklist said nothing), the EU controller identity and the email route, and removes the assumption that the policy would be hosted by the extension's own repository (it is Cloudflare Pages).
11. Change log
- 1.0 (2026-10-18): first approved revision. Decisions recorded on issue #109.