Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@

### Changed

- **The privacy policy rests on two laws: Zimbabwe's Cyber and Data
Protection Act [Chapter 12:07] and the EU and UK GDPR**, applied to everyone.
A new "The law we follow" section names POTRAZ as Zimbabwe's regulator, says
where data goes and how transfers out of the EU and UK are covered, and the
rights section lists each right, the one-month answer and where to complain
(POTRAZ, the ICO, or an EU authority). Each legal basis carries its GDPR
article. A breach goes to POTRAZ within 24 hours, as Zimbabwe's Act requires,
and to an EU or UK regulator within 72 where the GDPR requires it. Singapore's
PDPA is no longer named. The facts live once, in `dataProtection`
(`src/data/legal.ts`).
- **The legal pages match the extension's code, for 0.8.2 and 0.8.4**, and
are dated 6 October 2026. Checked against the extension's data schema
(`docs/toddle-data-fields.md`) and its code:
- Student flags: from 0.8.4 they are shown as Toddle shows them, and the
free switch hides them everywhere; versions before 0.8.4 hid them by
default. Unless flags are hidden, the sidebar asks Toddle for a
student's flags each time it opens.
- Memory: answers stay in the tab's memory until it is closed or
reloaded, reused for at most 5 minutes (2 for a student's day). Not
"a few minutes, then discarded".
- Storage: every key, named — the last revocation check and the Toddle
account's email in the extension's storage, and the four values on
Toddle's site (`tee-settings`, `gbx-hide-flags`, `tee-course-view`,
`tee-academic-year`). The "style of Toddle's message button" value it
once listed does not exist.
- Every switch, including gradebook tools and the Attendance dashboard's
student details.
- Licence keys may, not must, name who they are for.
- What each feature reads (gradebook, home page, profile page, Attendance
dashboard, sidebar), and the sign-in headers and academic year noted
from Toddle's own requests.
- Exactly what leaves the device, student photos included.
- The Security page covers the adversarial review of 6 October 2026
(findings 13 to 16) and what code inside Toddle's page cannot promise.
- **The audit sets one advisory aside, by ID, until 2026-11-03** (CI only).
GHSA-ch52-4w7c-c8xp in `http-cache-semantics` has no patched release, and
astro 7.3.5 uses the package only to cache remote images during
Expand Down
6 changes: 4 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
This repository is `learning.nyuchi.com`: a static Astro site, served by
Vercel, and the home of the Toddle Enhancement Extension.

The extension's own security model, how it is tested and its independent
adversarial review are published for schools at
The extension's own security model, how it is tested, and its adversarial
reviews of 1 October 2026 (0.8.2) and 6 October 2026 (0.8.4, before it is
published), with their findings and the limits of code that runs in Toddle's
page, are published for schools at
[learning.nyuchi.com/legal/security](https://learning.nyuchi.com/legal/security).
The vulnerability disclosure policy, covering this site, the extension and the
licence server, is at
Expand Down
4 changes: 4 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
site: "https://learning.nyuchi.com",
/* Astro's HTML compression drops the space where a line break sits between
text and an inline tag ("Applies to<strong>…"), across the legal pages.
The pages are small; correct words matter more than a few bytes. */
compressHTML: false,
adapter: vercel(),

// Fully static: every page is known at build time, so there is nothing to
Expand Down
93 changes: 59 additions & 34 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ community-based platforms. Nyuchi Web Services is its development division.
item, so a teacher sees the whole class at once instead of opening a side
panel per student. It also adds a switch that hides Toddle's student flags
across the product, so a gradebook can be projected or screen-shared without
a flag being visible. Flags are hidden by default; a staff member shows them
deliberately, with the switch or an eye button for one student.
It hides flags, not names. Free: the flag switch and a "My classes" filter on the
Toddle home page. With a licence: the per-criterion gradebook columns, CSV
export, primary teacher names, and the student sidebar. The sidebar (version
0.8.2) works anywhere a student appears in Toddle (class pages, the
Attendance dashboard's Periods, Students and Excusals tabs, name links,
a flag being visible. From version 0.8.4, flags are shown as Toddle shows
them, and a staff member can hide them everywhere with the switch, which is
free; an eye button then shows one student's flags. Versions before 0.8.4
hid them by default. It hides flags, not names. Free: the flag switch and
a "My classes" filter on the Toddle home page. With a licence: the
per-criterion gradebook columns, CSV export, primary teacher names, the
Attendance dashboard's details, and the student sidebar. The sidebar
(since version 0.8.2) works anywhere a student appears in Toddle (class
pages, the Attendance dashboard's Periods, Students and Excusals tabs, name links,
gradebook student cells) and shows a photo, grade, age and class of, Student
Group, room number, homeroom advisor, contacts with relationship, phone and
email, a "Now" card (current attendance code, block, class, room and
Expand All @@ -37,30 +39,46 @@ community-based platforms. Nyuchi Web Services is its development division.
tab, each student's year group and current class and teacher appear under
their name, and the current block's column is outlined. It reads Toddle in
the teacher's own browser, for the teacher at the screen, through an exact
allowlist of read-only queries sent only to Toddle, and never writes.
Nothing it reads leaves the browser; it transmits nothing from Toddle to
anyone: no account, no analytics, no tracking. Licence keys are checked
offline on the device; an individual key carries the buyer's email and an
organisation key the school's email domain, compared with the signed-in
Toddle account inside the browser only. An organisation licence is one key
for the school's email domain: anyone signed in to Toddle with an address
at that domain is covered. Student flags are hidden by default
and fetched only when a staff member chooses to show them. It has two
outside connections, neither carrying Toddle data. (1) Opt-in feedback: the
toolbar menu's "Send feedback" form, which, only when the person presses
allowlist of read-only queries sent only to Toddle, and never writes. It
reads, for the teacher's own account: assessment results, descriptors and
rubric levels as the school defined them in Toddle; class staff, for
primary teacher names; the Attendance dashboard's year groups and the
teacher of each student's current class; and, in the sidebar, the
student's details, contacts, classes, today's timetable and attendance,
and their flags when they are shown. It notes the sign-in headers and
academic year from Toddle's own requests only to ask Toddle as the
teacher; it never asks for a password. Nothing it reads leaves the browser
except back to Toddle; it transmits nothing from Toddle to anyone else: no
account, no analytics, no tracking. Student photos load from wherever
Toddle serves them, as on Toddle's own page. Licence keys are checked
offline on the device; a key carries the buyer's email and may name who
it is for (the buyer's email, or for an organisation licence the school's
email domain), which is compared with the signed-in Toddle account inside
the browser only. An organisation licence is one key for the school's
email domain: anyone signed in to Toddle with an address at that domain is
covered. Unless flags are hidden, the sidebar asks Toddle for a student's
flags each time it opens, and keeps them only while it is open. Apart
from Toddle, it has two outside connections, neither carrying Toddle
data. (1) Opt-in feedback: the toolbar menu's "Send feedback" form, which, only when the person presses
Send, sends what they typed (topic, message, and an email only if they
enter one) plus the extension's version number to Nyuchi's feedback form,
processed by Formspree; no student data, no page address, no licence key.
(2) Only while a licence is entered, once a day, the background worker
makes one plain request to https://licences.nyuchi.dev/v1/revocations,
(2) Only while a licence is entered, at most once a day, the background
worker makes one plain request to https://licences.nyuchi.dev/v1/revocations,
sending no licence key, no identifiers, no cookies and no Toddle data; it
downloads a Nyuchi-signed list of SHA-256 fingerprints of cancelled keys
and checks its own key locally. Cloudflare sees the IP address as with any
web request; Nyuchi does not log it. If the check fails, the extension
keeps working. The welcome page it opens on
install is part of the extension and sends nothing. It stores, on the
user's own machine, only its switch settings and the licence key if there
is one; no student data is stored on the device. Rubric levels and
web request; Nyuchi does not log it. If the list cannot be fetched, the
last result stands. Anything else (a CSV file, an email or phone link, a
message in Toddle's own chat) happens only when the teacher does it. The
welcome page it opens on install is part of the extension and sends
nothing. It stores, on the user's own machine, its switch settings, the
licence key if there is one, the result of the last check for cancelled
keys, and the email of the Toddle account last seen signed in (to check
who a licence is for); and, in Toddle's own site storage, a copy of the
switches, whether flags are hidden, the My classes choice and the academic
year Toddle is showing. No student data is stored: what it reads stays in
the tab's memory until the tab is closed or reloaded. Rubric levels and
descriptors are read from whatever a school has configured in Toddle; none
are defined in the extension. Security: https://learning.nyuchi.com/legal/security

Expand All @@ -76,7 +94,7 @@ community-based platforms. Nyuchi Web Services is its development division.

## Legal

All dated 1 October 2026. Operated by Nyuchi Web Services, the development
All dated 6 October 2026. Operated by Nyuchi Web Services, the development
division of Nyuchi Africa (Private) Limited, Harare, Zimbabwe.

- [Privacy policy](https://learning.nyuchi.com/legal/privacy): what the
Expand All @@ -100,8 +118,10 @@ division of Nyuchi Africa (Private) Limited, Harare, Zimbabwe.
next daily check for cancelled keys.
- [Data handling](https://learning.nyuchi.com/legal/data): for schools. What
the extension reads, where it is processed (only in the teacher's browser),
what it stores (settings and licence key; no student data), what leaves the
browser (only requests to Toddle, plus opt-in feedback), what Nyuchi holds,
what it stores (settings and licence details; no student data), what leaves
the browser (requests to Toddle and student photos from Toddle, plus
opt-in feedback and the data-free daily cancelled-keys check), what Nyuchi
holds,
the services that process it, retention, breach notification, and a data
processing agreement on request.
- [Student privacy](https://learning.nyuchi.com/legal/student-privacy): the
Expand All @@ -116,11 +136,15 @@ division of Nyuchi Africa (Private) Limited, Harare, Zimbabwe.
checked offline; no student data stored),
how it is tested (closed-loop tests, adversarial-review regression tests, an
end-to-end network recorder that fails on any non-Toddle request, every
release gated on them), the independent adversarial review of 1 October 2026
(two High, one Medium and several Low findings, all fixed in 0.8.2 with
tests), and the residual limit (scripts Toddle itself loads share the page
and the teacher's access regardless of any extension; Toddle's pages send no
Content-Security-Policy).
release gated on them), two adversarial reviews (1 October 2026: two High,
one Medium and several Low findings, all fixed in 0.8.2 with tests;
6 October 2026: two Medium and two Low findings in the code for 0.8.4, all
fixed before it is published), and the residual limits (scripts Toddle
itself loads share the page and the teacher's access regardless of any
extension; Toddle's pages send no Content-Security-Policy; a script
already listening in Toddle's page can replay the extension's per-load
secrets in that browser or answer its queries with false data, but cannot
make anything leave the browser or write to Toddle).
- [Vulnerability disclosure policy](https://learning.nyuchi.com/legal/vulnerability-disclosure):
report to security@nyuchi.com with the subject "Security"; acknowledgement
within 3 working days, triage within 10; safe harbour for good-faith
Expand Down Expand Up @@ -150,7 +174,8 @@ Please keep one distinction straight, because it is the one people get wrong.
This WEBSITE uses Google Analytics, behind a consent banner: it sets no cookies
and measures nothing unless the visitor presses Accept. The EXTENSION does not
measure anything at all, with or without consent. The extension contains no analytics, no telemetry and no
tracking, and nothing it reads leaves the browser it runs in. Its only outside
tracking, and nothing it reads leaves the browser it runs in, except back to
Toddle. Its only outside
connections are feedback a person writes and chooses to send from its menu,
and, while a licence is entered, a daily download of a signed list of
cancelled keys that sends no data. A question about
Expand Down
2 changes: 1 addition & 1 deletion src/components/CookieBanner.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* 2. Nothing optional starts on. Every toggle in the panel is unchecked until
* someone checks it, so closing the banner without answering leaves you
* where rejecting does.
* 3. The record carries a timestamp and a version. All three regimes put the
* 3. The record carries a timestamp and a version. Both laws (legal.ts) put the
* burden on us to show consent was given, and "the cookie was there" is
* not that.
*
Expand Down
17 changes: 7 additions & 10 deletions src/data/consent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/**
* What this site asks consent for, declared once.
*
* Three regimes apply to the people who read this site — staff at
* international schools — and they agree on more than they differ:
* Two laws set the standard (legal.ts, dataProtection): Zimbabwe's Act,
* because Nyuchi is Zimbabwean, and the EU and UK GDPR, the most widely used,
* applied to everyone. They agree on more than they differ:
*
* UK/EU GDPR consent must be freely given, specific, informed
* and unambiguous; granular per purpose, not bundled;
Expand All @@ -11,12 +12,8 @@
* Zimbabwe CDPA 12:07 "freely given specific and informed indication";
* withdrawal; the controller must be able to show
* consent was obtained
* Singapore PDPA purposes notified before collection; consent not a
* condition of service beyond what is reasonable;
* withdrawal honoured
*
* The shared requirements drive the design, so one implementation serves all
* three: nothing optional is on until it is chosen, each purpose is chosen
* The shared requirements drive the design, so one implementation serves both: nothing optional is on until it is chosen, each purpose is chosen
* separately, refusing everything is one press, and the record carries a
* timestamp and a version so it can be produced later.
*
Expand All @@ -27,9 +24,9 @@
* two third parties.
*
* This is the engineering, not legal advice. Someone qualified should read the
* privacy policy before it is relied on, and the PDPA separately expects a named
* data protection officer whose business contact details are published — that is
* a person to appoint, not a thing to code.
* privacy policy before it is relied on. Zimbabwe's licensing regulations for
* data controllers also expect a data protection officer to be appointed — a
* person to appoint, not a thing to code.
*/

export type ConsentCategory = {
Expand Down
Loading
Loading