Data processing
What we would hold on your students, and the country it would sit in.
This is not a signed data processing agreement, it has not been through a lawyer, and it is not in force, because there is nothing running for it to govern. Written on 5 August 2026. Plausible’s version of this page ends by saying that use of the service constitutes acceptance and no signature is required; we cannot honestly say that, because there is no service to use.
So what follows is the design, published early on purpose. Every measure described below exists in the code today and every gap is named as a gap. The point of publishing it before the first customer is that you get to read it in your own time instead of asking for it and waiting, and that everything on it can be checked against what actually arrives.
One consequence worth stating first: there is currently no student personal data in this system at all. No tenant exists, no student has ever signed in, and the cloud project holds one bucket containing nothing but the infrastructure code’s own state file. No database, no service, nothing running.
The roles
Who is the controller, and who is not
- You
- The controller. Your students are your students, your staff are your staff, and the record of who studied what is yours. You decide what is collected and why, and we act on your instructions.
- Us
- The processor. We hold that record only because we run the software it sits in. We have no use of our own for it and no product that would make one.
- Your students and staff
- The data subjects. A student never has an account with us — the account is with your school, the screen carries your name, and nothing in it names a supplier.
- Sub-processors
- The companies we use to run the software. There are four that could touch your data and one that is switched off; all five are named below, with what each one does.
- What we are not
- We are not a controller of your students, we will not contact them, and we will never market to them. We have no course of our own to sell, which is the reason the promise is easy to keep rather than a matter of restraint.
What would be held
Every category of personal data in the design
- 1
Who your staff are
An email address, a display name, whether the identity provider asserted the person controls that address, which sign-in method was used, when they were last seen, their role in your organisation, and whether they are active or suspended. - 2
Who your students are
An account and an enrolment row: which student, which published version of which course, which intake, when they enrolled, and whether the enrolment has finished or expired. - 3
What a student did
Per lesson: a status, cumulative seconds spent, when it was last opened and when it was completed. The seconds are written as increments rather than recomputed, which means the number is a sum of study time and not a surveillance trail. - 4
What a student answered
One row per attempt at a question: what was chosen, whether it was right, which attempt number it was, how long it took, and when. Attempts are numbered so that a first attempt stays distinguishable from a third. - 5
What your staff did
An audit row for the actions that change who can do what or what a student sees: who did it, which action, to which thing, and the request it belonged to. The free-form part of that row never carries a credential or a raw email address. - 6
Nothing else
There is no phone number, no postal address, no date of birth, no photograph, no free-text profile and no payment detail anywhere in the schema. Not held rarely, not held optionally. There is no column for any of them.
Where it sits
The United States, and there is no European option yet
The whole stack is pinned to one Google Cloud region, us-central1, in the United States. That is not a default nobody looked at: the infrastructure code contains a validation rule that refuses every other value at plan time, and the message it prints when it refuses says that European residency is served later by a separate project reusing the same module, rather than by making one project span two continents.
So if data residency inside the EU is a condition of the sale, today the honest answer is that we cannot meet it, and you should know that now rather than after a proposal. The work to meet it is a second project and a second set of buckets, and the design was built to allow that; it has not been done, and we are not going to describe it as though it had.
| Region | us-central1, United States |
|---|---|
| European region | Not available. A second project, not yet built |
| Multi-region | No. One region for every component, deliberately |
| Database | Cloud SQL for PostgreSQL 17, reachable only over an encrypted connection |
| Network exposure of the database | None. No permitted-network list exists, and none may be added |
| Your book and your questions | One storage bucket per environment, versioned, public access blocked outright |
| Encryption at rest | Google-managed keys. No customer-managed key is configured |
| Backups | 7 retained in the lower environments, 30 in production |
| Point-in-time recovery | Production only, with 7 days of transaction log |
| Running in production today | Nothing. Zero services, zero databases, one bucket |
Sub-processors
Every company that would touch it
- Google Cloud
- Runs the interface, the document worker, the database, both storage buckets, the job queue, the secrets and the logs. United States, us-central1. This is the one that matters; everything else on this list is small beside it.
- Firebase Authentication
- Also Google, inside the same project. Holds the sign-in credential for every account, staff and student alike, and sends the one email this product causes to exist, which is a password reset link.
- Vercel
- Serves the web application, including this page. It holds no database credential and no administrative credential: everything it needs it asks our own service for. The region has not been chosen yet — the plan is the one nearest us-central1, measured rather than assumed.
- Sentry
- Error reports, and only when a reporting address is configured. None is configured in any environment today, and with none the reporter is inert rather than quiet. When it is switched on, every payload passes through a scrubber that removes credentials both by field name and by shape, with no way for a caller to opt out of it.
- Document AI and Vertex AI
- Switched off in development, staging and production alike, by one flag that also withholds the permissions. Nothing in the product calls a language model on any code path. If that ever changes, this list is the first place it changes, and before rather than after.
That is the whole list. There is no marketing platform, no email service, no support desk, no chat widget and no content delivery network beyond the one that comes with serving the web application. Each of those is a company that would be on this list if it existed, which is most of the reason none of them does.
Two names sit in an awkward middle and belong here rather than in a footnote. The code has typed integration points for Sentry, for error reports, and for PostHog, for product analytics — interfaces with no-op implementations, no SDK installed in any bundle, and no key configured. Nothing is sent to either company today and nothing can be until somebody sets a key, at which point they become sub-processors and this page has to say so. We would rather name them now than have you find them in a dependency list later.
Measures
What actually stops one provider reading another
- 1
The database refuses an unscoped query
Every tenant-owned table carries the organisation it belongs to, and the policy on it compares that against a value set for the duration of one transaction. When the value is not set the comparison is neither true nor false, so the statement returns nothing. Unset means see nothing, never see everything. - 2
The service cannot switch its own policies off
Two database roles. One owns the tables and runs migrations; the other runs the service and is created without the privileges that would let it bypass a policy. The running system never holds the credential that could turn the mechanism off. - 3
There are no key files
The infrastructure resource that creates a downloadable service account key is banned in this repository and appears in it only in comments saying so. Every identity is a short-lived token issued at the moment it is used. A key file is a credential that can be copied; there are none to copy. - 4
Buckets are closed rather than configured closed
Public access prevention is enforced, permissions are set at the bucket rather than per object, versioning is on, and a deleted object is recoverable for a window. A misconfigured object cannot be made public, because the setting that would allow it is refused at the bucket. - 5
Suspension takes effect immediately
Every credential issued to a person at or before the moment they are suspended is refused from that moment. Not when their session happens to expire. This is the thing everyone assumes already works, and usually does not. - 6
Logs are redacted before they are written
Authorisation headers, cookies and tokens never reach a log line, and raw email addresses are deliberately kept out of a store with a long retention period and broad read access. The error reporter applies the same rules again, by field name and by value shape, because an exception carries things a log statement never chose to include.
Four things a signed agreement contains that this page does not, and each is unwritten rather than omitted: how much notice you get before this list of sub-processors changes; a breach notification timetable in hours; a right of audit and what it would let you inspect; and the standard contractual clauses that a transfer to the United States needs. All four need the registered entity and a lawyer. Ask about any of them and you will get the same answer as this page gives, which is what is true today.
The measures above, at more length, together with where your own material sits and what is only a commitment so far. Security and data
Send one chapter.
Not a demo of somebody else’s course. A chapter of yours, converted properly, so you can look at your own questions and your own page numbers and decide whether it is any good. Pick the chapter you know best. If it is no good, you have lost an email.
We will ask who owns the copyright before we convert anything. If you do not hold the rights to the material, we will say so rather than take the work.