One school cannot reach another
Every record carries the school it belongs to, and every query is scoped to the school of whoever is signed in. A request for another school's record is answered as not found rather than not allowed — because refusing tells you the thing exists.
How: Tenant scope applied at the query, not filtered afterwards; cross-school reads return 404.
Access is derived from one place
What a person can reach follows from their role through a single capability layer, rather than each screen deciding for itself. A permission that no page honours, or a page no permission guards, is a defect we can find rather than one a school discovers.
How: A capability map drives both the menu and the server-side gate, and a test refuses a permission with no reachable page.
Consequential actions are written down
Publishing a policy, withdrawing a document, changing what someone can see, exporting data — each leaves a record of who did it and when. Not because a regulation asks, but because the first question after anything goes wrong is what happened.
How: A server-side audit trail written by the API itself, not by the screen that called it.
The AI is bounded by the same rules
The AI has no privileged view of your school. It uses tools chosen by the asker's role, scoped to the asker's school, and every consultation is recorded — so what the AI looked at can be inspected afterwards.
How: No AI tool accepts a school id from the caller; role is checked before execution and refusals are logged.
Diagnostics without the child in them
When something breaks we keep enough to fix it and nothing more. Tokens, email addresses, query strings and identifiers are stripped before an error is stored, and we do not record which person hit it.
How: Scrubbing runs before the write, not before the display — so the sensitive value never lands in the database.
Backups that have been restored
Encrypted with AES-256 and — the part that matters — actually restored into a scratch database, with the row counts compared, and the steps written down. A backup nobody has restored is a hypothesis.
How: openssl AES-256 with a key held off the database host; a documented restore drill with the known traps listed.
Hardened in the browser too
Pages are served under a strict content security policy where every script must carry a per-request nonce, so an injected script does not run even if one is somehow introduced.
How: Nonce-based CSP minted per request; pages render dynamically so no page can ship without one.
Accounts a school controls
Single sign-on against your identity provider, and access that ends when someone leaves — because the most common way school data escapes is an account nobody closed.
How: SSO against your provider; role and access changes recorded in the audit trail.