Subprocessors

Every third party that receives anything, and what each one gets.

Every row below was verified by reading the code at the file and line named in the last column. Nothing here is from memory or from a vendor's marketing page.

Effective date: 4 September 2026 Last verified against the code: 4 September 2026

This is the companion to privacy-policy.md. If a provider is not on this list, it does not receive your information — there is no fourth analytics vendor, no error-tracking SDK, no advertising network, and no data broker anywhere in CourseLite.


How to read the "role" column

  • Processor — handles data on CourseLite's instruction, for CourseLite's purposes.
  • Independent controller — decides for itself what to do with the data it receives, under its own policy. You have a direct relationship with them.
  • Both — processor for the part we instruct, controller for its own operational and security purposes. Most infrastructure providers are here.

The role assignments below are our reading, not a legal conclusion, and have not been reviewed by a lawyer or confirmed against each provider's DPA. See the note at the end.


1. Providers that receive user data

ProviderPurposeData categories it receivesRolePrivacy policyVerified in code
Clerk (Clerk.com, Inc.)Authentication, sessions, sign-up and sign-in UI. Also sets the sign-in cookie on courselite.org and, in the extension, is the only reader of itEmail address, password or social sign-in, session token, user id, and whatever Clerk's own bot check collects. Our server additionally asks Clerk for a user's email address and roleIndependent controller for the account; processor for the id we read backhttps://clerk.com/legal/privacyfunctions/lib/auth.ts (the only place identity is established); src/main.tsx:33 (ClerkProvider); functions/lib/admin.ts:91-92 and functions/api/courses/ingest.ts:451 (createClerkClient, the only server-side Clerk lookups)
OpenAI (OpenAI, L.L.C. / OpenAI Ireland Ltd)Chat answers, embeddings, image transcription, generated documents and decksCourse material text, retrieved passages, assignment briefs, the student's questions, the student's and instructor's names, and images nothing else could parse. No account identifier is sentProcessorhttps://openai.com/policies/privacy-policy · API data: https://developers.openai.com/api/docs/guides/your-datafunctions/lib/openai.ts:170 (the single fetch, authorization + content-type only, no user: field); functions/lib/vision.ts
Stripe (Stripe, Inc. / Stripe Payments Europe Ltd)Subscription checkout, billing portal, payment processingEmail address, CourseLite user id (as client_reference_id), plan, and the card details you enter on Stripe's own pageIndependent controller (payments); processor for the ids we sethttps://stripe.com/privacyfunctions/lib/stripe.ts:34 (API base), :196–203 (customer create, sends email), :272 (client_reference_id: input.userId), :133 (the single fetch)
Google LLCDrive/Docs/Slides access under drive.file, the file Picker, and Google FontsThe drive.file OAuth grant, the files you pick or that CourseLite creates, and the content written into them. Separately, your IP address and user-agent when the Inter webfont loads. Google does not tell CourseLite which account granted the accessdrive.file carries no identity and no openid/userinfo.email scope is requestedIndependent controllerhttps://policies.google.com/privacyScope: extension/manifest.template.json:29, extension/src/google/auth.ts:47, extension/src/google/token.ts:50, src/google/picker.ts:37. Fonts: index.html:33–36
Cloudflare, Inc.Hosting the website and the API (Pages + Pages Functions), CDN, TLS, DDoS protection, and the Turnstile bot checkEvery request: IP address, URL, headers, and request body. Turnstile additionally receives the challenge token and the IP for one verificationBothhttps://www.cloudflare.com/privacypolicy/Deploy target throughout functions/; wrangler.toml; public/_headers; Turnstile: functions/lib/turnstile.ts:75, :137, :144
Neon, Inc.The PostgreSQL database — where everything CourseLite stores actually livesAll stored data: course material, documents, embeddings, generated work, account rows, analytics, anti-abuse hashes, billing idsProcessorhttps://neon.tech/privacy-policyfunctions/lib/db.ts:90 (connect(), the single connection point); docs/DATABASE.md
Crossref (Publishers International Linking Association, Inc.)Resolving a DOI into a bibliography entryA DOI, and nothing else. Re-normalised before the call, so a caller holding an essay gets a null instead of a request. A user-agent naming CourseLite is sent; deliberately no email addressIndependent controllerhttps://www.crossref.org/operations-and-sunsetting/privacy/functions/lib/sources.ts:222–252 (crossrefByDoi, CROSSREF_ORIGIN), reached from functions/api/sources.ts actions doi and url

2. Providers involved in operating CourseLite, that do not receive user data

ProviderPurposeWhy it is not a subprocessorVerified in code
GitHub (Microsoft)Source hosting and CI. Typecheck, build, deploy on push to mainThe pipeline builds and deploys code. It holds no runtime secret and never touches production data — DATABASE_URL is a Cloudflare Pages runtime secret, never a CI secret.github/workflows/ci.yml, .github/workflows/deploy.yml
npm registryBuild-time dependenciesBuild machine onlypackage.json, package-lock.json

3. Providers deliberately not used

Worth writing down, because the absence is the disclosure.

Not usedWould have been for
PostHog, Mixpanel, Amplitude, Google Analytics, PlausibleProduct analytics. Ours is two Postgres tables (usage_events, usage_daily) written from one file
Sentry, Bugsnag, Rollbar, DatadogError tracking. Ours is one Postgres table (app_errors) plus Cloudflare's own logs
Any advertising network, ad SDK, or attribution providerNothing. There are no ads and no advertising code
Any data broker, enrichment or identity-resolution providerNothing
Any device-fingerprinting or bot-scoring vendor beyond Cloudflare TurnstileAnti-abuse. Ours is five one-way hashes of relationships you chose
Any email marketing platformNothing. Transactional email only
Redis, KV, or any caching serviceNothing. There is no cross-request cache for user-scoped data, deliberately
Any object storage (S3, R2, GCS)Nothing. No file bytes ever reach a CourseLite server

The outbound destinations, counted by reading rather than by grepping. There are five, and the fifth is the one a grep misses:

DestinationWhere the call isHow the host is chosen
api.openai.comfunctions/lib/openai.ts:170DEFAULT_BASE_URL in functions/lib/models.ts:36, overridable by OPENAI_BASE_URL
api.stripe.comfunctions/lib/stripe.ts:133STRIPE_API, a module constant (:34)
challenges.cloudflare.comfunctions/lib/turnstile.ts:144SITEVERIFY, a module constant (:75)
api.crossref.orgfunctions/lib/sources.ts:230CROSSREF_ORIGIN, a module constant (:252). The call is options.fetchImpl ?? fetch, so a search for fetch( does not find it — an earlier audit missed this row for exactly that reason
Clerk's APIInside @clerk/backend: verifyToken (functions/lib/auth.ts:20) fetches Clerk's signing keys, and createClerkClient(...).users.getUser (functions/lib/admin.ts:91-92, functions/api/courses/ingest.ts:451-452) reads a userThe SDK's own constant, derived from CLERK_SECRET_KEY. No fetch( appears in our source for it at all

No user-supplied URL is ever fetched server-side, on any of these paths.


4. Where the data physically sits

Where
Website and APICloudflare's global edge network
DatabaseNeon, in the United States (AWS us-west-2, Oregon)
Model inferenceOpenAI's infrastructure
AuthenticationClerk's infrastructure
PaymentsStripe's infrastructure

Assume the United States unless a region is filled in above, and see section 15 of the privacy policy.


5. Changes to this list

If we add a provider that receives user data, we will add it here and update the privacy policy before it starts receiving anything.


6. What is not verified

Kept separate from the table on purpose, so nobody mistakes one for the other.

  • The role column is our reading. It has not been checked against any provider's data processing agreement, and none has been signed or reviewed.
  • No DPA is in place with any provider named above. Several offer one. If CourseLite ever serves users in the EEA or the UK, a signed DPA with Clerk, Neon, OpenAI, Stripe and Cloudflare is a prerequisite, not a nicety.
  • No international transfer mechanism has been confirmed for any provider.
  • Neon's point-in-time restore window is Neon's to set, not ours. The region is stated above; the length of the backup window is a schedule Neon operates and we do not control, which is why neither this page nor the privacy policy quotes you a number of days for it.
  • Sub-processors of our providers — the people Clerk, Neon, OpenAI, Stripe and Cloudflare use in turn — are listed on their own pages and are not reproduced here. Each provider maintains its own list.
  • The legal entity names above are the ones the providers publish. Confirm which entity actually contracts with CourseLite before relying on them in a contract.
  • Sub-processor lists change without telling us. This page is a snapshot of what the code does on the date at the top, not a subscription to each provider's own list.

7. Contact

A question about anything on this pagesupport@courselite.org
Notice that a provider's details here are out of datesupport@courselite.org
Privacy questions generallysupport@courselite.org — see the Privacy Policy

CourseLite is an online service with no public office, so there is no postal address to write to. Email reaches a person.

If you are a school or a university evaluating CourseLite, this page and the Privacy Policy are the two documents to read together, and the honest summary is the one in section 6: no data processing agreement has been signed with any provider named above, and no international transfer mechanism has been confirmed. If your institution requires either, CourseLite does not meet that requirement today, and we would rather you learn it here than after a procurement review.