Privacy policy
This policy explains what data Backthread collects, why, how long we keep it, who else sees it, and the rights you have over it. We try to write this in plain language. If you read something here and the Security page contradicts it, the Security page is the implementation truth — tell us so we can fix the language.
1. Who's the controller
BACKTHREAD OÜ, registered in the Estonian Commercial Register under number 17524049, with registered office at Harju maakond, Tallinn, Lasnamäe linnaosa, Peterburi tee 92g, 13816, Estonia ("we", "us", "Backthread"), is the data controller for the data described in §2.2, §2.3, §2.4, §2.6, §2.7 and §2.8 below.
For the source code we momentarily clone when you connect a repository (see §2.5), we act as a data processor on your instructions: you decide what code we read and why. The companion Data Processing Addendum sets out our obligations as your processor.
Contact for any privacy question, request, or complaint: hello@backthread.dev.
2. What data we collect and why
2.1 Waitlist signups (controller) — removed 2026-08-20
There is no waitlist any more, and we no longer collect any of this. The form had already been taken off every page; on 2026-08-20 we deleted the endpoint behind it, removed its database and rate-limit bindings, and deleted the notification it sent. Until then it was still reachable, so we are naming it rather than quietly dropping the section.
While it existed, joining the waitlist recorded your email address, which form
variant you used, any UTM parameters, your referrer, user-agent and
Accept-Language header, the approximate country, city, region,
timezone and ISP that Cloudflare reports, and a salted SHA-256 hash of your IP
address for rate limiting — never the raw IP. The lawful basis was consent
(Art 6(1)(a) GDPR). Sixteen entries were collected in total, between
2026-05-17 and 2026-06-25, and nothing has been collected since.
§10 records what happened to them.
2.2 Account data (controller)
When you sign in:
- your GitHub OAuth identity (GitHub user ID, login, email, avatar URL) via Supabase Auth;
- the session token Supabase issues, which lives in your browser;
- an account record (one personal account per signup; the unit of data isolation in our database).
Lawful basis: contract performance (Art 6(1)(b)).
2.3 GitHub installation linkage (controller)
When you connect a repository via the Backthread Ingest GitHub App:
- the GitHub installation ID GitHub assigns to your install;
- which repositories you granted access to (owner + name + default branch + visibility);
- sync status per repo (
pending,syncing,ready,failed,disconnected) and the latest sync error, with credentials scrubbed.
We do not store your GitHub App private key (it lives in our orchestration Worker as a secret) and we do not persist installation access tokens — they are minted per ingest job and destroyed with the sandbox.
Lawful basis: contract performance (Art 6(1)(b)).
2.4 Derived diagrams + changelogs (controller; processor for team customers)
For every connected repository we store:
- the derived diagram (modules, edges, clustering, layout) — JSONB;
- the per-module changelog with the LLM-narrated "why" behind each PR-merged change;
- the flows and decisions the system derived.
This is the product. We do not store your source code in this category — only what we derived from it.
Lawful basis: contract performance (Art 6(1)(b)).
2.5 Source code (processor)
When an ingest runs, the orchestration Worker spawns a fresh, isolated sandbox (a Cloudflare Container — Firecracker microVM); inside it we:
- clone your repo with
git clone --depth 1using a job-scoped installation token; - read the source statically (no
npm install, norequire()of your code, noeval); - write only derived data to our database;
- destroy the sandbox.
The clone, the installation token, and any in-memory representation of your code die with the sandbox. We do not retain your source code in any database, log, or cache outside the lifetime of that single sandbox. See Security for the implementation detail.
When we process source code, we act as your processor under Article 28 GDPR. See the Data Processing Addendum.
2.6 Operational / transient data
To keep the service running we keep a few short-TTL records:
- per-IP rate-limit counters on the signup endpoint (10-minute TTL);
- per-webhook delivery dedupe markers for GitHub webhook replay protection (10-minute TTL);
- per-repo in-flight markers for queue dedupe (10-minute TTL);
- operational logs in our worker tier — credentials scrubbed before write — typically retained ≤ 30 days by Cloudflare.
Lawful basis: legitimate interest (Art 6(1)(f)) — operating and securing the service.
2.7 Page-view analytics (controller)
Both backthread.dev and app.backthread.dev
load Cloudflare Web Analytics — a third-party script
served from static.cloudflareinsights.com. On
app.backthread.dev Cloudflare injects it at the edge, so it
does not appear in the source we publish. It did the same here until
2026-08-21, when a change to our deployment configuration switched the
injection off without warning; we now carry the tag in our own HTML
instead, so on this site it is visible in the source we publish as well
as in the raw HTML of any page we serve. Either way, the
Security page shows you how to check. Per page
view it reports:
- the path you are on, its host, and the referrer;
- page-load and responsiveness timings your browser measures anyway (LCP, CLS, INP and similar), plus low-level navigation metadata such as transfer sizes and the connection protocol;
- for those timings, a CSS selector identifying which element on the page was slow — a description of our own markup, not of you;
- your browser's JavaScript memory-usage figures (heap used, total, and limit);
- a random id generated fresh for that single page load, transmitted and then gone.
The measurements are sent to cloudflareinsights.com/cdn-cgi/rum.
Your IP address and user-agent are not collected by the
script, but they reach Cloudflare in the HTTP request headers, exactly as
they do for every request to any website.
It is cookieless: it writes no cookie, no
localStorage entry and no sessionStorage
entry, and it mints no identifier that outlives the page load — so it
cannot join one visit to the next and cannot follow you to any other
site. We use it for exactly one thing: counting how many people reach a
page. On app pages the reported path contains the repository slug you are
viewing. Cloudflare serves every page of the app, so it already receives
those paths when a page is loaded; the script additionally reports paths
you reach by navigating inside the app, which the edge would not
otherwise see. Either way the recipient is Cloudflare, which is already
our host and our sub-processor — the script adds no new
recipient, though it does report a little more to that one.
Lawful basis: legitimate interest (Art 6(1)(f)) — knowing whether anyone reads our pages. Article 5(3) of the ePrivacy Directive gates two things: storing information on your device, and gaining access to information already stored there. The script stores nothing, and what it reads is generated by the page load itself rather than retrieved from anything kept on your device, so we treat it as outside the consent gate. We want to be straight that this is our reading, not a settled question — a regulator could fairly press on the second limb, since the script does read your browser's performance and memory figures. If a supervisory authority tells us we are wrong, we will put it behind consent or drop it, and say so here.
Cloudflare injects a second script at the edge, which we
also had not noticed until 2026-08-17:
Email Address Obfuscation, served from our own origin at
/cdn-cgi/scripts/…/email-decode.min.js. It rewrites
mailto: links and bare addresses into a
/cdn-cgi/l/email-protection link and decodes them in your
browser, so that address scrapers do not harvest our inboxes. It
collects nothing, sends nothing, and stores nothing. Its side-effect was
on you, not us: with JavaScript disabled, the address you need in
order to exercise any of the rights in §7 did not render at all.
Since 2026-08-21 every address on this policy, the
DPA, the Terms and the
Security page is excluded from the obfuscator
and is served as an ordinary mailto: link that works with
JavaScript switched off. The obfuscator still runs elsewhere on the site.
Addresses are additionally written out in plain text in the imprint at the
foot of the Security page.
Those two are the complete set of scripts we serve anywhere, as of the date
at the top of this policy — but they are no longer both on every page.
Since 2026-08-21 the obfuscator is injected only where an address still
needs obfuscating, and none are left on this policy, the
DPA, the Terms or the
Security page, so those four load the analytics
beacon alone. Listing every <script> tag in any
response we serve is still the check to run: expect one
on the legal pages, and on the home page that one plus the obfuscator
plus a short inline script of our own that loads nothing and contacts
nobody — three tags, two of them third-party. We will not add
another without naming it here first.
2.8 Billing data (controller)
If you subscribe, payment is handled by Stripe — through Stripe Checkout and the Stripe billing portal. Stripe collects your card details directly; they never reach our servers and we never see or store a card number. What we keep is the subscription state Stripe reports back to us: your Stripe customer and subscription identifiers, plan, seat count, status, and invoice history.
Lawful basis: contract performance (Art 6(1)(b)), and compliance with our accounting obligations (Art 6(1)(c)) for the invoice records.
2.9 Lesson answers and grades (processor for team customers)
If you use the lessons, we keep the free-text answer you typed, the grader's verdict on it, and the grader's note. The answer is sent to Google to be graded; your identity is not sent with it (see §5 and §11). The account-scoped deletion that applies to a lesson answer is described on the Security page.
Lawful basis: contract performance (Art 6(1)(b)) — providing the product your account signed up for. For a team account we process these as processor on the account owner's instructions under the DPA.
3. What we do not collect or do
- We do not use first-party tracking cookies. On this website the only thing we put in your browser is one
localStorageflag remembering that you dismissed the cookie banner. In the signed-in app, Supabase Auth additionally keeps your login session inlocalStorage(§2.2) — strictly necessary to keep you signed in. Nothing else, and nothing that tracks you. - We do not use fingerprinting, advertising pixels, cross-site trackers, or session replay, and no analytics we run can join one of your visits to another or follow you to another site. (Your login session deliberately persists between visits — that is §2.2, and it is how staying signed in works.)
- We do count page views, with a cookieless third-party script on both of our domains — see §2.7, which describes exactly what it reports. Until 2026-08-17 this section said we used no third-party analytics at all. That was untrue: Cloudflare injects the script at serve time, so it was absent from our source and we did not notice it was there. §10 records the correction.
- We do not sell, rent, or share your data for advertising.
- We do not train any model on your code or your prompts. We pass enrichment and decision-derivation context to Google and Anthropic under agreements that exclude training on API content (see §5).
- We do not persist your source code outside the lifetime of a single ingest sandbox.
4. How long we keep things
| Data | Retention |
|---|---|
| Source code in the ingest sandbox | Destroyed at end of job (job timeout ≤ 10 minutes) |
| Derived diagrams + changelogs + flows + decisions | Until you disconnect AND explicitly ask us to delete (we keep derived data after a disconnect so you can reconnect without losing history; you can ask for deletion any time) |
| Account data | Until account deletion (self-serve from /account or via email) |
| GitHub installation linkage | Until you uninstall the GitHub App AND ask us to delete the linkage row |
| Waitlist signups | Deleted 2026-08-20 — the waitlist was removed and the entries with it (§2.1, §10) |
| Per-IP rate-limit hashes | 10 minutes |
| Webhook delivery / queue-dedupe markers | 10 minutes |
| Operational logs (worker tier) | ≤ 30 days at Cloudflare |
| Lesson answers, verdicts + grader notes (§2.9) | Until the person leaves the account, or the account is deleted, or you ask us to delete them |
| Billing records — subscription state + invoices (§2.8) | Held by Stripe as well as us; we keep invoice records for as long as Estonian accounting law requires (7 years), even after account deletion |
| Page-view analytics events (§2.7) | Held by Cloudflare, not by us — we store none of it ourselves and cannot lengthen or shorten their retention. Per Cloudflare's documentation: unsampled for 7 days, then aggregated for longer-term storage, with roughly the previous six months visible to us |
Where we delete data, we delete it; where the rule requires us to retain it (e.g. accounting records for tax purposes), we keep what the rule requires for as long as it requires.
5. Who else sees it (sub-processors + recipients)
We use the following sub-processors — providers that process data on our behalf. Each operates under a Data Processing Agreement. With Google and Anthropic those are the providers' own published standard terms, linked in the table, rather than agreements we negotiated line by line — and we want to be exact about what that does and does not mean, because we used to be vague about it. Each of those addenda takes effect by being incorporated by reference into the commercial terms we accepted when we opened the account: no separate signature is required for either addendum to bind. Both providers do offer an acceptance step — Google's addendum refers to a partner having “clicked to accept”, and Anthropic's own help page is titled “how do I view and sign” — so “unsigned” is not quite the right word either; what is accurate is that neither addendum waits on a negotiation or a countersignature from us. Whether that is as good as a bespoke agreement is a judgement we are not going to make for you in our own privacy policy; what we can do is name the exact instrument and link it, which the table does. What differs between providers is the transfer mechanism, so the table names each one specifically instead of claiming Standard Contractual Clauses across the board. Separately, Cloudflare's page-view analytics (§2.7) appears in this table for completeness even though we run it as controller, not on any customer's behalf — the DPA §6 explains why it is not a sub-processing activity. Recipients that are not sub-processors are listed separately after the table.
| Provider | Role | What they see | Where |
|---|---|---|---|
| Supabase, Inc. | Database, auth, realtime | Account data, derived diagrams, derived changelogs, flows, decisions and their risk items, and lesson answers + grades (§2.9) | EU region (eu-west-1, Ireland).
DPA ·
Trust Center |
| Cloudflare, Inc. | Pages, Workers, Queues, KV, D1, and cookieless page-view analytics (§2.7) | Worker job buffer, transient dedupe / rate-limit data, and page-view analytics events (path, host, referrer, user-agent, load timings) | Global edge, with the EU jurisdiction option for our D1 bucket. DPA · Sub-processors |
| Google LLC | LLM naming + narration of the diagram, decision derivation over redacted capture transcripts, and writing + grading the lesson questions in the app | Diagram enrichment: module + file identifiers, edge metadata, PR titles + bodies. Decision derivation: redacted capture transcripts (natural-language only; no source code, no tool I/O). Lessons: the recorded decision a question is written from, and — when grading — the answer you typed. Your identity is never sent with it | US. Google is our processor under its Data Processing Addendum for Products Where Google is a Data Processor (Google's own service list is what puts “Gemini API Paid Services” in its scope), which the Gemini API terms incorporate by reference. It covers paid Gemini API use specifically. Our project runs far past the free tier's daily limit, so paid is the only way our usage could be served — but that is our reading of our own bills, not something we can show you from this page, and we would rather flag it than let it pass as settled: if it were ever wrong, this addendum would not apply and Google's unpaid terms permit training on what is sent. For transfers it applies a Data Transfer Solution where one exists (the EU–US Data Privacy Framework) and Standard Contractual Clauses where one does not. |
| Anthropic Ireland, Limited (its Commercial Terms name this entity for customers in the EEA; Anthropic, PBC elsewhere) | LLM tie-break for decision derivation from redacted capture transcripts | Redacted capture transcripts (natural-language only; no source code, no tool I/O) | US processing. Anthropic's DPA, incorporating EU Standard Contractual Clauses (Module 2 and/or Module 3, to the extent applicable data protection law requires), is automatically incorporated into its Commercial Terms of Service — accepting those accepts the DPA, with nothing to sign. DPA |
| GitHub, Inc. | OAuth identity + the read-only GitHub App | Your GitHub identity + the source code you grant the App to read | US. EU SCCs under the GitHub Data Protection Agreement. |
| Stripe | Payments + subscription management (§2.8) | Your billing name, email, address and card details — collected by Stripe directly, never by us. Nothing from your repositories | Contracting entity and region are set out in the DPA linked here — for EU customers Stripe contracts through its Irish entity. EU SCCs under the Stripe DPA. DPA · Privacy Center |
| Resend | Delivering the emails we send you — the Decision Digest and trial notices | Your email address and the contents of the email, which for the digest includes decision titles + summaries derived from your repository | US, per the DPA linked here. EU SCCs under the Resend DPA. DPA |
We push internal operational alerts into five private Telegram channels — errors, signups, product, marketing and ops — and one of our own internal tools also posts a nudge into Telegram. We name the operator as Telegram FZ-LLC, which is the Dubai group company; Telegram's own policy names Telegram Messenger Inc. as the controller. We have not confirmed which entity we contract with, and we would rather say that than pick one and sound certain. This section used to say that was one direct message about a new waitlist email, and that Telegram never saw customer data. Both were wrong — and the waitlist itself is now gone (§2.1), so that message is no longer sent at all. Rather than give you a count of messages we cannot verify, here is every kind of thing that goes there, read out of the code that sends it:
- a new app signup — the email address, the GitHub username (or the display name, if they signed in another way) and the sign-in provider;
- product events — a repository's first successful build, an App uninstalled, a subscription started or cancelled, a usage milestone. These carry the repository's full name, including private repositories belonging to our customers, plus the GitHub account or organisation login, account, installation and user identifiers, and counts. This particular announcement fires on a repository's first successful build — but a repository that never builds is not thereby unnamed, because the failure alerts below name it precisely when a build goes wrong;
- failure alerts — the error text, the repository the failure happened on, and a link to that repository in the app. This has included customers' private repositories — in 2026-07 and 2026-08 we sent failure alerts naming two private repositories belonging to one customer. The error text is whatever the failing system handed back, so it can contain paths and pull-request numbers from inside the repository that failed;
- service alerts that are not about one repository — a scheduled job that did not run, a digest run that did not go out, a payment webhook we could not process. These carry counts about customers (how many digests were missed, for instance), Stripe invoice and event identifiers, and the raw error text from the system underneath;
- a disputed grade — the repository's identifier, the question's identifier, the verdict you disputed, and the length of the note you wrote;
- a nudge that our own marketing tooling has work waiting — a count of unreviewed items and a link to an internal page. No data about you is in it, but it is a message to the same place, so we list it;
- our internal sweeps — a daily marketing sweep, weekly engineering and production-health summaries, and a monthly strategy review. Between them they carry funnel counts, new signups, repositories connected, subscription revenue and infrastructure health; short verbatim quotes from public posts on Hacker News, Reddit and blogs, with their authors' names, none of whom we are talking to; and research notes that can name companies and individuals we are talking to.
We are not going to tell you how many messages there were, and you should know why. We do not keep the messages we send. Some of these paths leave a record of the failure that triggered them, but that record is written whether or not the message actually went — and messages are de-duplicated before sending, so even the records that exist do not map one-to-one onto anything Telegram received. Three earlier drafts of this section carried a precise message count. Each was wrong, in a different way, because it was counted from something that was not the thing being counted. What we can stand behind, and have named above, is that particular repositories were named and roughly when — read from the repositories and their build failures, not from a tally of messages. Even that is a floor rather than a total: some repo-naming alerts (a stalled capture, a frozen decision log) write no record at all, so there may have been more.
What never reaches it: your source code, your captured transcripts, and the text of a lesson answer. The dispute alert above carries the note's length, never its words — though that was not true before 2026-08-19, and §10 says what changed.
We treat Telegram as a recipient, not a sub-processor, because it is a messaging channel we push notifications into under its own terms rather than a service processing your data on our behalf to deliver the product. That label does not shrink what it receives, and it operates under no Data Processing Agreement with us — we would rather write that down than let the word "recipient" imply a protection that is not there. If you'd rather we didn't notify the founder about your signup, tell us at hello@backthread.dev and we'll turn it off for you.
Two answers in the FAQ on our home page can play videos from
YouTube (Google), through the
youtube-nocookie.com privacy-enhanced player. Each one shows
a play button, not a player: the embed is not in the page
until you press it, so your browser sends nothing to Google
unless you choose to watch. Once you do, Google receives that
request and with it your IP address and user-agent, and whatever the
player then stores in your browser is Google's behaviour, not ours — we
will not characterise it on their behalf. We treat Google
as a recipient here rather than a sub-processor, because
this is our page, not your data. You can also skip the embed entirely:
the timestamped links beside each player open the same moment on YouTube.
We should say why this is worded so carefully. Until 2026-08-17 both
videos were ordinary embeds marked loading="lazy", and this
policy said your browser therefore contacted YouTube only if you scrolled
to them. We measured it, and that was false — browsers
do not defer iframes the way they defer images, so both players were
fetched on every home-page load, more than six thousand pixels below the
fold, one of them inside a collapsed answer. Every visitor's IP address
went to Google. We rebuilt them as click-to-load rather than write the
exposure down and leave it in place.
Apart from the two Cloudflare scripts in §2.7 and those two video
embeds, the pages on backthread.dev request
nothing from a third party: fonts are served from our
own origin, and there are no external images and no tag manager. Every
other external address on those pages is an ordinary link that loads
only if you click it. The signed-in app at
app.backthread.dev necessarily talks to more: our own
ingest worker, Supabase, and — only when you open billing — Stripe.
We will publish at least 30 days' notice on this page before adding a new sub-processor that processes customer data. (Telegram is intentionally not in this list because it is a recipient rather than a sub-processor — not because what it receives is trivial. That is set out above.)
6. International transfers
We are established in Estonia; our primary data store (Supabase) is in
the EU (eu-west-1, Ireland).
Some sub-processors are US-headquartered (Cloudflare, Google, Anthropic, GitHub, Stripe and Resend — Anthropic contracts through Anthropic Ireland, Limited and Stripe through its Irish entity for EU customers, as §5 says). For those transfers we rely on the European Commission's Standard Contractual Clauses (SCCs, Commission Implementing Decision 2021/914) incorporated into each sub-processor's DPA — with one exception we should not bury: Google's processor terms apply a Data Transfer Solution (the EU–US Data Privacy Framework) where one is available and fall back to SCCs only where it is not, so for Google the mechanism may be the Framework rather than the Clauses. §5 names it per provider. All of it is supplemented by the supplementary measures described on Security — the most important of which is that we never persist your source code, which sharply limits what a US authority can compel about it.
One transfer is not covered by that: the internal alerts we push to Telegram (§5), which is outside the EEA and has no Data Processing Agreement with us and therefore no SCCs. We are naming it here rather than letting the paragraph above read as if it covered everything.
A summary Transfer Impact Assessment is available on request to hello@backthread.dev.
7. Your rights
Under the GDPR + the Estonian Personal Data Protection Act (PDPA, 2018) you have the right to:
- access the personal data we hold about you (Art 15);
- rectify inaccurate data (Art 16);
- erase your data (Art 17) — subject to retention rules we have to follow;
- restrict processing (Art 18);
- port your data to another service (Art 20) — we'll export your diagrams as JSON on request;
- object to processing based on legitimate interest (Art 21);
- withdraw consent at any time, for processing based on consent (Art 7(3));
- lodge a complaint with the Estonian Data Protection Inspectorate (Andmekaitse Inspektsioon, Tatari 39, 10134 Tallinn, info@aki.ee, +372 627 4135) or with the supervisory authority in your EU member state of residence.
To exercise any of these rights, email hello@backthread.dev. We'll respond within one month, per Art 12(3); if your request is complex we may extend by two months and tell you why. We do not charge for handling a request unless it is manifestly unfounded or excessive.
8. Security
We describe what we do — and what we don't do — on the Security page. Highlights: TLS in transit; encryption at rest by our cloud providers; ephemeral sandboxes for source code; least-privilege GitHub App scope; secrets only inside the worker tier; safety budgets against pathological inputs.
If you think you've found a security issue, write to security@backthread.dev (or hello@backthread.dev) and we'll respond within one business day. We do not yet run a bug bounty.
9. Children
Backthread is for adults. If we learn we've collected data from a child under 16 without parental authority, we'll delete it.
10. Changes
We'll update this page when we change a load-bearing fact and bump the date at the top. For material changes affecting your rights, we'll email the address on your account at least 14 days before the change takes effect.
2026-08-21 — we said every sub-processor has a DPA without having
checked which one, and we had the wrong country on the page that says
where your data lives. §5 asserted that each sub-processor operates under a Data
Processing Agreement "relying on EU Standard Contractual Clauses where data
leaves the EU." We went and read the actual instruments. Three things were
wrong. One: we linked the wrong agreement for Google. We
cited the Google Cloud Data Processing Addendum; we do not use
Google Cloud or Vertex AI, we use the Gemini API, and its terms name a
different document — the Data Processing Addendum for Products Where Google
is a Data Processor. The Cloud addendum does not list our service anywhere.
Two: Google's transfer mechanism is not simply "EU SCCs" —
that addendum applies a Data Transfer Solution such as the EU–US Data
Privacy Framework where one exists, and falls back to Standard Contractual
Clauses where it does not. Anthropic's genuinely is SCCs, Modules 2 and 3.
Three: Anthropic's own Commercial Terms say that for a
customer in the EEA the contracting entity is Anthropic Ireland,
Limited, not Anthropic, PBC. We are in Estonia. We had named the
wrong company. And separately, this policy, the DPA and
the Security page all said our database sits in
eu-central-1, Frankfurt. It sits in eu-west-1,
Ireland. Both are in the EU and no data moved — but we had told you the wrong
country, and we would rather correct it than let it stand because it looked
harmless.
On the underlying question — whether a published addendum nobody
countersigned is really our agreement — §5 now states the mechanism
(incorporation by reference into commercial terms we accepted) and links the
instrument, instead of hedging in one document and worrying in another. The Cloudflare row also no
longer lists lander signups, which stopped existing when we deleted the
waitlist.
2026-08-21 — the address you need in order to use §7 did not
render without JavaScript. Cloudflare's Email Address
Obfuscation rewrote every address on these pages into a link that only a
script could decode, so a reader with JavaScript switched off saw
"[email protected]" where our contact address should have been —
including in §1 and §7, which is where a data subject is told how to reach
us. §2.7 had disclosed the script since 2026-08-17 but treated the
consequence as an inconvenience to us. It was not: it made the contact
route the law requires unusable for anyone browsing that way. Every
address on this policy, the DPA, the
Terms and the Security page
is now excluded from the obfuscator and served as a plain
mailto: link. We kept the obfuscator running on the rest of
the site rather than switching it off in a dashboard, because a setting no
file in our repository records is not something we can promise you about —
the exclusion lives in the pages themselves, and a check in our deploy
script fetches the published pages afterwards to confirm a working
address is really there.
2026-08-21 — three corrections to what we published yesterday. Yesterday's Telegram correction, below, was checked again — this time against the deployed code rather than the copy of the source we happened to have — and three things did not survive. One: the Security page told you that a connected repository which never builds is never named to Telegram. That is false, and §5 on this page already said so: failure alerts name a repository precisely when its build goes wrong, so a repository that never builds successfully is named the first time it fails. The Security page now matches. Two: §5 said one of our internal tools sends its nudge to the founder's direct message. We could not verify that — the destination is a deployed secret, and the last time we described one of those from the name in the code we published something false — so we have removed the claim rather than repeat the mistake. The nudge still goes to Telegram, and it still carries no data about you. Three: §5 and the Security page said Telegram never receives your source code. We have stopped writing never there. We do not send it your code, but a failure alert carries a tail of the error output from a tool that was reading your code, and we cannot rule out that a line of it appears in one. A sentence in §5 that an earlier edit had left garbled has also been repaired.
2026-08-20 — we removed the waitlist and deleted what it had collected. The sign-up form had already been taken off every page, but the endpoint behind it was still live and still accepting email addresses that nothing on the site linked to. We deleted it, removed its database and rate-limit bindings, deleted the Telegram notification it sent, and deleted the credentials it used. Sixteen entries had been collected between 2026-05-17 and 2026-06-25; all sixteen have been deleted, and §2.1 and §4 now say so. The lawful basis for holding them was your consent to join a waitlist, and there is no waitlist to join, so continuing to hold them was not something we could justify. If you gave us an email address that way, we no longer have it.
2026-08-20 — a correction, and an admission: we understated what Telegram receives, and until 2026-08-19 it received more than this. §5 and the Security page both said the founder gets one Telegram message when a new email joins the waitlist, and that Telegram never sees customer data. Both halves were wrong. It goes to five private Telegram channels — errors, signups, product, marketing and ops — plus a nudge our own marketing tooling posts when it has work waiting. They receive the email address and GitHub username on each app signup other than our own test fixtures; product and failure alerts carrying repository names — including private repositories belonging to our customers; service alerts carrying counts about customers and Stripe identifiers; account, installation and user identifiers; and our daily marketing sweep, weekly engineering and production-health summaries and monthly strategy review. §5 sets out each kind in full. It deliberately gives no counts: we do not keep the messages we send, and three earlier drafts of that section each published a precise number that turned out to have been counted from something that was not the thing being counted.
The part that is not merely a disclosure fix. Until 2026-08-19, disputing a graded answer sent Telegram up to 300 characters of the note you typed and 200 characters of the question — the exact text the Security page says is readable only by you. We changed the code that day so the alert carries the question's identifier, the verdict and the note's length only, and we verified the deployed worker no longer contains the old text. So "never the words" is true now, and it was not true before 2026-08-19. We are stating that rather than letting the present tense cover it. One thing we can tell you, as far as our records go: not one of the lesson answers we currently hold has ever been disputed — so on that evidence the words in question were never actually sent. We say "as far as our records go" precisely: answers are deleted when an account is deleted or a person leaves a team, so the rows we hold today cannot prove what happened to rows we no longer have. The defect was real; the exposure, on the record we have, was not. We are reporting both halves, because reporting only the reassuring one is how the original sentence went wrong.
Nothing about what we store or how long we keep it changed, and no new sending started. Telegram remains a recipient rather than a sub-processor, and §5 now says it operates under no Data Processing Agreement with us, because the label was doing work the facts did not support. That has a consequence we should not leave for a reader to find: Telegram is outside the EEA, so this is a transfer with no Standard Contractual Clauses behind it. §6 and the DPA describe SCC coverage for our sub-processors, and we have added a sentence to each saying plainly that this recipient is not one of them. Narrowing what we send there is tracked as work, not as a claim.
2026-08-19 — a removal, named rather than made quietly. §2.9 used to say that who can see a lesson answer was described on the Security page. We removed that pointer, because we removed the paragraph it pointed at. Both said, in different words, that a person's coverage is visible to nobody but them. That stopped being true on 2026-08-12, when we decided a team's lead can see an individual's coverage, and it shipped shortly after. Nothing about what we store or how long we keep it changed, and the deletion rules still apply and are still on /security. What changed is that we no longer make a promise the product does not keep. The lesson answers we store, their verdicts and the grader notes remain readable only by you. Three actions publish what you typed, on purpose, and all three are yours to take: recording an answer as a decision, disagreeing with something we recorded, and flagging a question as bad. Telling us a grade was wrong is not one of them — that note stays on your own answer row, readable only by you. On a public repository a recorded answer is readable by anyone, and so is a disagreement filed against a decision; a disagreement filed against a graph anomaly stays with account members, and the kill record is never public. A recorded answer can also reach teammates in the daily digest email.
2026-08-17 — a correction. §3 claimed we used no third-party analytics. That was false the whole time it was published: every page on both of our domains, this one included, was serving Cloudflare's cookieless page-view script. Cloudflare injects it at the edge, so it appeared in no file we publish and repeated reads of our own source could not find it — we found it by reading a live response. Rather than quietly soften the sentence, we are naming what happened. In the same pass we added §2.7 describing precisely what that script reports, named the role in §5, disclosed the two YouTube embeds on our home page as a recipient, and added Google's lesson question-writing and grading role to §5 and §11 — it was described on the Security page but missing from this one and from the DPA.
Auditing that correction turned up three more things, all fixed on the same date, and all worth naming rather than absorbing quietly:
- Cloudflare injects two scripts, not one. Our first attempt at §2.7 said the analytics beacon was the only one. It is not — there is also an email-address obfuscator. Same blind spot, twice: both are added at the edge and neither is in any file we publish.
- The video embeds were not deferred. We had described them as loading only if you scrolled to them. Measurement showed they loaded on every home-page visit, sending every visitor's IP to Google. They are now click-to-load, so the description is true because the behaviour changed, not because the wording did.
- Stripe and Resend were missing entirely. Payments and the emails we send you were live and named in no policy. They are now in §2.8, §5 and the DPA §6. We found them only because we had just promised §5 lists every recipient, and then went to check.
11. AI Act transparency
The Backthread product uses a general-purpose AI model (Google Gemini) to name and narrate the modules + per-module changelog you see in the diagram. The diagram structure is derived deterministically from your source code; the model never authors it. Strings the model produced are tagged in the UI so you can question them. This disclosure is provided in advance of the Article 50 EU AI Act transparency-obligation deadline (2 December 2026 per the May 2026 Commission consultation).
The same model also writes and grades the lesson questions in the app: it drafts a question from a decision already recorded in your repository, and when you answer, your typed answer is sent to be graded and a verdict plus a short note comes back. Your identity is not sent with it. A grade is a model's judgement, not a fact about you — the app shows you the grader's note precisely so you can disagree with it.
BACKTHREAD OÜ · registration number 17524049 · registered office Harju maakond, Tallinn, Lasnamäe linnaosa, Peterburi tee 92g, 13816, Estonia · hello@backthread.dev