Legal
Security
The real posture, including the parts that are not finished.
Not ready to sign
This document describes what the product actually does, and it is not executable yet. Still missing:
- a registered address
- an address for privacy questions
- a review by a qualified lawyer
The data we hold is deliberately thin
The strongest security control in the product is that most of the sensitive data a school holds is never given to us. Nalda holds public content a school already publishes, what a family typed, and contact details a family volunteered. No student records, no grades, no attendance, no health information, no application files. There is no import path for them and no field to put them in.
A breach of Nalda would expose enquiry conversations and contact details. That is bad. It is not a student information system.
Separation between schools
Every row in the database carries the school it belongs to. Every query the application makes goes through one access layer that pins the school in the query builder rather than in the calling code, so a query cannot be written without it.
That is enforced by a build gate: writing a database call outside that layer fails the build. Row level security is switched on for every table with no policies, which denies everything to the public and signed-in database roles, but the application connects with a service role that bypasses it. So the honest description is that separation between schools is enforced in application code and by a build gate, and the database is a deny-by-default backstop for anything that is not the application. Per-school database policies are not in place yet. We say that here rather than describing it as enforced by the database, because the difference matters to somebody evaluating us.
Encryption
In transit: TLS on every connection, to the application and to the database.
At rest: the database and its backups are encrypted at the storage layer by the hosting provider.
Additionally at the application layer: the school's Google Calendar tokens are encrypted with AES-256-GCM using a key that lives only in the environment, so a database dump of that table is inert without it.
Not yet: family contact fields are not separately encrypted at the column level. They are protected by the storage layer encryption and by the access controls above. Column level encryption of contact fields is intended and is not built.
Append-only records
The ledger, the staff audit log, the answer traces and several evidence tables have UPDATE and DELETE revoked at the database level from every role, including the one the application uses. TRUNCATE, TRIGGER and REFERENCES are revoked too, because each of those is a way to reach update or delete sideways.
So the record of what the assistant answered, and of what a member of staff did, cannot be rewritten by the application, by an operator, or by us.
Access control
Staff hold a role: owner, admin, staff or viewer. Every privileged action goes through one wrapper that resolves the caller and their role first, runs the work second, and writes the audit row third. A build gate fails any server action or route that parses input before it has authorized the caller.
Identity is resolved against the authentication server on every request rather than read from a cookie.
Internal operator access to the ops console is limited to an explicit list of email addresses held in the environment. Unset means nobody, never everybody.
The assistant's own boundaries
A message from a family is treated as hostile input. It is screened for injection, it grounds only on retrieved content, it reaches no tool that can send, book or write, and links in an answer are restricted to the school's own domain and our booking page.
No language model sits in a write path. Models draft and classify. Anything that sends, books or writes a record is deterministic code behind an explicit state machine and, where it matters, a person pressing a button.
Every factual sentence must bind to a passage of the school's own content or it is removed and the question goes to a person. Money figures and dates must match the cited source exactly rather than approximately.
Secrets
Credentials live in the environment only. Every variable the application may read is declared in one contract that fails the build when a required one is missing, and an unset variable never degrades into a permissive default. A secret scanner runs on every build and no credential is committed to the repository.
Retention and deletion
Conversations are purged on a per-school schedule, 24 months by default. Any household can be exported and deleted by school staff from inside the product, and the deletion is verified afterwards by searching for the family again rather than by trusting the delete statement.
What we do not have
No SOC 2, no ISO 27001, no third party penetration test. The product is built to be certifiable later and is not certified now.
No single sign-on for staff in production yet. The settings page for it exists; the integration does not.
No formal incident response plan on paper, beyond the 48 hour notification commitment in the data processing agreement.
No bug bounty. Security reports are welcome and are treated as defects.
Reporting something
If you find a security problem, tell us before telling anybody else, and we will fix it and say what happened. The contact address goes here once it is published.