Tutus · school safeguarding · described field for field
A student can file a report here without signing in. Here is exactly what that report carries, and what it does not.
A reporting line that overstates itself is not a small mistake — it is a child deciding whether it is safe to say something, on the strength of a sentence somebody wrote in a hurry. So this page does the opposite of a promise: it names the route, the fields, the four layers of request plumbing that would otherwise have captured the reporter, the four build checks that hold those closures shut, and the three storage questions that are still open. Every capability sentence here is a branch of what the serving code actually does today.
Two reporting routes, neither with a sign-in on it: one for a link that carries the school, one for the plain link with no school. A separate, sign-in-gated console for reading reports. A follow-up path that does not exist, said so plainly. Money honest-off.
What happens when a student opens the link
A reporter arriving on a link that carries their school can file a report today, with no sign-in, wherever the intake check passes. Whether anyone can then open it is a second check, described in the module section below.
- The link carries the school. A reporter opens /report-a-concern/<school> — the school part is the public storefront link, the same one that appears in a school’s own web address. It is not the internal record id, and this surface never handles one. The page reads it straight out of the address bar.
- They fill in two things. a concern category from a list of six, and the tip text itself. There is no name field, no mail field, no phone field, and no field for an internal school or student record. Nothing about who is reporting rides along, because there is nowhere on the form for it to go.
- The school rides along as institutional context. which school (the public school link from the poster, never a reporter identity). It says which school, never who is reporting.
- The session is dropped on the way out. A reporter may be a student who is also signed in to the school platform in the same browser — and the whole point is that this must not matter. The reporting page strips that session before the message is sent: the submit call sets a hard override that stops the shared request helper attaching the bearer token it would otherwise default to, so the Authorization header is not sent. That override is held by its own test at the real header-building boundary, and by a build check of its own.
- The server takes it with no sign-in requirement. The submit handler carries no session check, no bearer requirement and no staff role. Its own header records why the obvious module guard is not used here: that guard throws an unauthorised error on a null context, which would have made this intake unreachable for a logged-out reporter — forcing exactly the sign-in this line exists to avoid. The module check is done a different way instead (see the module latch below).
- The answer is a receipt, not a token. On this link a successful report comes back as a created status with three keys: the report id, its triage state, and its lane label (a fourth appears only if the report’s audit entry fails to write). The page shows the id back. It is a receipt that the report was saved under the school. It opens nothing, and it does not say who can open the report.
Two things this line is honest about in the other direction. The plain link with no school on the end of it does not go to a school: it carries its own form, and a report sent from it is saved in the Stanley Studios triage queue, which is not any school’s triage list. If the page gets no clear answer from that intake, it says after the send that it cannot tell whether the report was saved, keeps what was typed on the page, and shows no receipt it did not get. And a school link whose intake check fails answers with the same not-found the platform gives an unknown school, on purpose; that is covered below too.
The reporting page itself opens with an emergency notice before the form, not after it: in an emergency, call your local emergency number first. Nothing here claims that anyone monitors the line, and this page will not imply it.
Field for field: what a report carries
The most useful thing a safeguarding vendor can publish is not a promise, it is a list. This is the entire wire body a report on a link carrying the school sends, and the entire set of things that are structurally not in it. Three layers hold the second list: the field does not exist on the input type; if a reporter field arrives anyway, the route refuses the report on its field name before anything is stored; and the strict schema refuses every other field it does not recognise.
Carried
- A concern category
One of six, from a closed list. Anything outside the list is refused by the schema, and the routing layer independently falls back to general triage on an unrecognised value rather than dropping the report. - The report text
Up to twenty thousand characters. Content, not identity. It is stored byte for byte as written — including a student’s account of a named adult — and who can open it depends on the three states described in the module section. - The public school link
Institutional. Which school, never who. Resolved server-side to an internal record under a system scope, because a reporter has no tenancy of their own and a link lookup must not depend on the caller’s.
Absent
- A name, a mail address, a phone
Not optional-and-blank. The input type the form fills in has no such field, so there is nothing to leave empty and nothing to redact later. - A session, a bearer token, a user id
The submit call drops the Authorization header explicitly, even for a reporter who is signed in elsewhere in the same browser. - The caller’s network address, in any log or key
The framework’s own request log is silenced on this route; the general limiter uses a salted bucket for it instead of the raw address; the route’s own bucket is salted, rotating and truncated. - An internal school or student record id from the reporter
The public link is the only school addressing this surface knows. A subject student, if one is named, is verified against the school and dropped if it does not match — the report is still filed. - Any unexpected key at all
The body schema is strict: an unknown field is a hard rejection, not a silent drop. Before the schema runs, the route checks field names against twenty reporter-field spellings and refuses the report with nothing stored if one is present, so a caller that spread a raw request body into the request fails loudly.
Two shape rules on the server are worth stating because they are what makes the walk-up call legal without a sign-in: exactly one school addressing field is required (never both, never neither), and exactly one narrative field. The walk-up client supplies the public link and the text, which satisfies both. Reports default to heightened confidentiality rather than the other way round.
The plain link with no school sends a smaller body: the category and the report text, and nothing else. Its schema is strict in the same way, so a contact field is a hard rejection there too, and it accepts one optional field more, a source host, which the reporting page does not send. The same reporter-field check runs first on that route, before its own insert, so all three layers hold there. Its success answer is two keys, the report id and its triage state.
A clean route is not a clean request
A reporting route can have no reporter field, write no reporter column, and stamp a null actor on its own audit row — and still sit underneath plumbing that captures the reporter on every single call. That is what an audit of this surface found, and it is the part of this work that is not visible from the outside. Three always-on layers were writing the reporter down one level up, and a fourth lived in the route’s own limiter.
The correction that matters most here is not any single closure — it is what happened when one of them was asserted rather than enforced. A route file’s own header claimed registry membership for two public paths from the day it landed. The entries were never added. The claim was a comment, not a control, and two layers stayed open in production while the file said they were shut. A build check now fails on exactly that: a route whose comments claim membership it does not have.
And the check that found the most was the one that asked the inverse question. Two of the checks take the registry itself as their population, which makes a route missing from the registry invisible to both by construction — a measurement that cannot enter its adverse state while the fault is present is not an alarm. A third check takes the other population (routes that shielded themselves at the log layer) and asserts set equality in both directions. Run bare, it exited non-zero on four orphans, one of them a public route that had been writing the raw caller address into shared storage on every request.
The registry’s members include all three routes a reporter on this line touches: the school-link submit (POST /v1/safety/tips), the plain-link submit (POST /v1/safety/tips/unrouted), and the poster-code lookup that runs before anything is typed (POST /v1/safety/tips/code). The other members are public intake routes elsewhere on the platform — question-technology, the public assessment, both halves of the image takedown channel, the public school ask route and others. This page names members rather than printing a total, because a total typed into a page goes stale the day a route is added.
The link cannot be used to ask questions about children
An open route that varies its answer with a submitted identifier is a lookup service for whoever holds the link. That is the failure mode a public reporting line invites, and every branch below was designed against it rather than discovered afterwards.
The rule that produces all of it: a status or a shape that varies with a submitted id is an oracle, so nothing here varies with one. A school link that does not resolve, a school link whose intake check fails, and a poster code that is tampered with or expired take the identical not-found, with the same message and the same body. A named subject student who belongs to a different school and a student id that does not exist converge on the same outcome — and, importantly, that outcome is not a refusal. The structured reference is dropped and the report is filed anyway, with the narrative untouched, because refusing would have thrown away a child’s report to protect a database column.
| What is submitted | A plain online form | This line |
|---|---|---|
| An unknown school link | distinguishable from a real one | uniform not-found |
| A real school link whose intake check fails | distinguishable | the same uniform not-found |
| A subject student who belongs to another school | rejected, so the answer varies | reference dropped, report still filed |
| A staff id a stranger guessed | stored, and it arms a wall | dropped; the narrative is untouched |
| The success body on a school link | echoes what was sent | the same keys whatever was sent |
The staff row deserves its own sentence, because it is the one where a protection could itself become the weapon. The structured staff-id field is the key to a recusal wall with four enforcement points. On an open route, that means a stranger who knew or guessed a serving administrator’s id could file a fabricated account naming them and, in the same call, wall that administrator off from ever seeing it. That was measured at the wire, not inferred: before the fix, a test persisted a victim’s real id and then returned a refusal to the victim on the report accusing them. Rejecting the field outright would have deprived a child naming a real abuser of filing at all. So the field is dropped, the words are kept, and the report is filed. The wall itself is untouched and still fail-closed — what was removed is a stranger’s ability to arm it.
Two checks decide what happens to a report on your school’s link
A report on a link that carries the school meets the safety module twice. The intake check runs when the report is sent: it reads the school’s own billing plan, or the free plan if the school has no billing account of its own. The read check runs on every staff list, single-report read and release: it reads the school’s own plan or, if the school has none, its studio’s plan, or the free plan if neither has one; then any module setting recorded for the school, for its studio or for the whole platform, which can disable the module for the school but cannot enable it over a plan that leaves it out; and, where a priced-license profile is recorded for the module, whether that license is held. The two checks read different things, so they can disagree, and that gives a school link three states.
- Both checks pass. The link takes the report and shows its receipt. The report is stored under the school; where both checks pass, the district’s safety administrator can open its text, and the school’s administrators can open it only after the district releases it.
- The intake check fails. The link answers with the same not-found an unknown school gets, and nothing is stored — deliberately identical, so the public address cannot be used to work out which schools are on the platform.
- The intake check passes and the read check fails. The link takes the report and shows the same receipt, and the report is stored under the school. In the third state, no one at the school or the district can open it: the list, the single-report read and the release all refuse. The reporter cannot tell this state from the first one. It happens, for example, when a school has no billing account of its own and its studio’s plan leaves the module out, or when the school’s own module setting disables it.
As the platform ships, three of the four base plans include the module and the fourth, curriculum_only, leaves it out, and an operator can change which modules a plan includes. A school whose own plan leaves the module out is in the second state. A school with no plan of its own, whose studio’s plan leaves it out, is in the third.
That means the honest instruction to a school has two steps. Send a test report through the link, and then have your district’s safety administrator sign in and open the school’s safety list. A receipt on the first step proves only that the intake check passed. The second step is the one that proves a report on this link can be opened, and this is what the staff console shows. If the list opens and shows the test report, both checks pass. If it shows This school’s safety list cannot be opened here, the read check refused the list: after a receipt on the first step, reports sent on that link are being stored where no one at the school or the district can open them, and that needs fixing before the link goes on a wall. If it shows You don’t have access to safety-tip triage for this school or Restricted -- school and district administrators only, the account’s role was refused; that answer says nothing about the module, so sign in as the district’s safety administrator and look again. A not-found on the first step means the link is not active: the school part of the address is mistyped, a poster code is mistyped or expired, or the intake check fails for your school. The answer does not say which, on purpose. There is no status page that would tell you this from outside, because a status page for this would be the enumeration oracle the design just spent all that effort closing.
This page does not know your school’s plan or its module settings and does not claim to. It states the three states, what each looks like from outside, and the two-step check that tells you which one your school is in.
The reason the intake check works this way rather than the ordinary way is worth recording, because the ordinary way had a defect in it. The platform’s standard module guard throws an unauthorised error when there is no session context — which, on a route designed for a logged-out student, meant the module check itself was forcing a sign-in. The intake was moved to a system-scoped read of the school’s own plan, so a logged-out reporter and a signed-in one get a byte-identical response. That read is narrower than the ordinary guard the staff side still uses, and that difference is where the third state comes from.
Where a report goes after the receipt
This section describes a report that arrives on a link or poster code carrying the school. A plain-link report is saved in the Stanley Studios triage queue instead, and nothing that happens to it there moves it into the lanes, the ledger or the district hold described here.
Routing is a pure function of the category. No reporter identity is consulted, because none exists to consult. The lane it picks is a label: the server names it in the answer it returns and in the report’s audit entry, and the stored report carries its category, not a lane. Every school-link report is stored under that school, whatever its lane. The form also labels four of the six categories urgent, from the category alone, and shows a reporter who picks one a prompt to call 911 if anyone is in danger. That label lives on the form only: the server stores no priority, and nothing reorders a queue by it.
| What the reporter picks | Label on the form | Lane label |
|---|---|---|
| Bullying or harassment | Standard | safety_triage |
| A threat of violence | Urgent | threat_assessment |
| Self-harm or suicide risk | Urgent | threat_assessment |
| A weapon on campus | Urgent | threat_assessment |
| Shared intimate images without consent | Urgent | ncii_trust_safety |
| Something else | Standard | safety_triage |
The underlying record vocabulary is wider than the form — the database also accepts a substance category and a general abuse category, which reach triage through staff-side entry rather than the public form. The form deliberately offers the smaller list, in a reporter’s words rather than a schema’s.
Threat assessment
A threat of violence, a weapon on campus, or self-harm and suicide risk route to the behavioural threat-assessment lane, and the receipt the server returns names that lane. The report is stored with its category under the school. Where both checks pass, a school or district safety administrator can see that it arrived, and its text is held until a district administrator releases it to the school. Where the intake check fails, the school link answers the uniform not-found and stores no report. Where the intake check passes and the read check fails, the report is stored and no one at the school or the district can open it. The server stores no priority for a report, and nothing on this line pages a person when one arrives. If anyone is in danger right now, the page tells the reporter to call 911. A report that is referred onward can attach to a threat case with its own state machine — referred, screening, assessing, managing, monitoring, closed — and its own risk levels and action vocabulary, including a recorded distinction between an assessment, a parent contact, a safety plan, a mental-health referral and a law-enforcement action.
Image-based abuse
A report of intimate images shared without consent is filed on the school’s safety line like every other school-link report, and the receipt the server returns names the image-abuse lane. The lane is a label on the receipt and in the report’s audit entry; the stored report carries its category, not a lane, and it is stored under the school like the others, so who can open it follows the same three states. It does not enter the public image takedown channel described further down this page. That channel is separate: its requests are stored in their own table with a forty-eight-hour deadline, and a school never reads them. A report on the school line does not open a takedown request, and a takedown request does not appear on a school’s list. A person depicted in an image who wants it removed from this platform files through the takedown channel.
General safety triage
Bullying and harassment, and anything the reporter classified as something else, are stored under the school, and the receipt names the general triage lane. The routing function takes a raw category string and falls back to this lane on anything it does not recognise, on purpose: an unknown value is still filed, not dropped as invalid. A report’s recorded state can be set to new, triaged, referred to a team, closed — or marked duplicate.
Staff misconduct escalation
A report that names a member of staff through the structured staff-id field gets the staff-misconduct lane label, regardless of its category, because an account of an adult must not sit in the in-house queue that adult helps run. Said plainly and in the present tense: on the public link the answer still names that lane, but the stored report carries no staff id, because that field is dropped on the open route. The report is stored under the school like any other, and the recusal wall has nothing to act on. The web reporting form never sends the field. Off-site delivery to an external or district authority is a provider seam that is not wired. What this lane owns today is the label, and the access-control decision for a report that carries a staff id.
Two things that happen to every school-link report, whichever lane it lands in
It is chained. Intake appends an entry to an append-only ledger, hashed to the school’s previous entry, so a replay detects an alteration or a removal from the middle of the chain. The entry is deliberately census-neutral: the category, the state, the lane, and booleans recording whether a subject or a staff member was named — never the report text, never an identifier. And the verify endpoint says what it cannot prove: its completeness verdict is null, meaning not measured, because an omission at the tip of the chain leaves the chain intact and there is no independent expectation to compare against. A consumer checking that verdict alarms rather than passes, and quieting it by narrowing the check back is the repair we will not make.
It arrives with no school grant. A walk-up report arrives with no school grant recorded. Where both checks pass, policy keeps it at the district until a district actor releases it, and a held report stays in the school’s list, stripped, with a flag saying so — not hidden. Hiding it would hide that a report about this school exists, and a school that cannot see a report exists cannot ask for it. The existence is the actionable part; the content is what is withheld. On the single-report read, a held report returns a truthful two hundred with the narrative and the subject stripped rather than a refusal, because a refusal there is mapped by the console to a sentence telling a legitimate administrator they have no access to triage at all — which would be false, and they would stop looking. Where the intake check passes and the read check fails, the list and the read refuse instead, for the district as well as the school.
The staff side is confidential and does require a sign-in
There are two submit routes — one for a link or poster code that carries the school, one for the plain link with no school — and neither has a sign-in on it. Everything on the READING side does, and that asymmetry is the design rather than an oversight: the person reporting is not the person being audited, and a staff read of a report about a child is exactly the event an access log should hold.
The list and the single-report read both require the safety module (the read check described above), a records-staff role, and a school scope, and every identity field on the way out routes through the same consent chokepoint the rest of the platform enforces — a suppressed student’s name is masked while the row still counts, so a redaction never silently changes a total. A report marked heightened-confidentiality has its body withheld on the LIST for every role, whatever their seniority; only the single-record read can reveal it, and reports default to that heightened setting rather than the other way round.
The recusal wall runs before any body or subject is assembled. A staff member named as the subject of a report is omitted from the list entirely — the accused must not even learn such a report exists — and refused on the direct read with no body leaked. It is fail-closed in the sharpest way: an unidentifiable viewer on a report that names someone is treated as recused rather than allowed. Reports that name nobody are untouched.
The same conflict rule is applied to a second, harder case: a member of staff who performed or recorded a use-of-force incident cannot be the one to close the documentation of it. Seniority is not independence. An unidentifiable actor is recused, and an unresolvable participant set is recused too, because the closer’s independence cannot be established — the wall errs toward exclusion rather than falling open.
Live today, behind a sign-in, scoped to one school, consent-gated on the way out.
What this is not: an emergency channel
In an emergency, call your local emergency number. This software records and labels reports; it does not dispatch, and nothing on this line pages a person when a report arrives.
The reporting page carries that notice above the form, not below it, and this page repeats it because a hallway poster is read by people in trouble. The related panic-alert surface is honest in the same direction, and the honesty is structural rather than a disclaimer.
A panic alert is recorded, given a map reference and a lifecycle, and fanned out to an internal responder audience as counts — never a roster. What it is not is delivered. The planner that builds the alert is a pure function: no network call, no clock, no provider underneath it. The public-safety integration is unset at the call site, so the external verdict comes back as unconfigured with a reason naming the missing integration. With no mail-provider key, each responder message is planned as queued and never marked sent. With no verified sending identity for the school, it is suppressed rather than faked.
The part worth reading twice: a lifecycle stamp that wrote a durable “the notification went out at this time” column was removed from this path. It fired on a plan, not a result — and an incident review or a subpoena reads that column as a delivery. Worse, the honesty of it was being held only by the absence of a provider key, so it would have started making a true-looking false claim silently on the day a key was provisioned. The stamp is now reachable only when a human asserts it under their own identity, behind a forward-only transition guard. The panic hardware and the emergency routing belong to a hardware partner; we are the record and notification layer, and this page will not blur that.
The rest of the safeguarding console the line sits inside
A reporting line on its own is a mailbox. What makes it useful is that the report lands in the same console as the drill log, the visitor record, the custody directive and the crisis headcount — and under the same consent and access walls. Each of these is shipped engineering with its own tests, not a roadmap slide; each is described here by what it does and by the constraint it was built against.
Visitor watchlist and front-office check-in
A kiosk checks a visitor in at the front office and screens against a watchlist the school manages directly, with a visitor log behind the same staff gate. It is the one safeguarding surface where the subject is an adult rather than a child, which is why it is a separate console rather than a tab on the student record.
Crisis command: accountability and reunification
During an incident, a live headcount of who is accounted for, missing, or off site, plus reunification progress. Counts only, never a who-is-where roster. That constraint is the whole design: a screen that shows where every child is, shown in a crisis to whoever is holding the tablet, is a different and worse artefact than a number that tells you how many are still unaccounted for.
Drills, sensor feeds and alert lifecycles
Drill scheduling and logging, and normalised sensor-detection events with an explicit notification lifecycle so an alert cannot sit unacknowledged with nobody owning it. The detection hardware is a device partner’s; the record, the lifecycle and the audit trail are ours.
Use-of-force documentation with an independence rule
Restraint and seclusion documentation carries a self-review recusal: the staff member who performed the incident, or who recorded it, is excluded from closing its documentation. Fail-closed on an unidentifiable actor and on an unresolvable participant set. Admin seniority does not substitute for independence.
Custody directives, hall passes, dismissal and bus ridership
The front-office safeguarding surfaces that share the same module and the same walls: recorded custody directives that the reunification release and the dismissal desk read server-side, hall passes, carline dismissal with a flat refusal for a barred guardian, and bus ridership. Staff-gated, office-gated, and scoped by the same representation wall as the student record.
What is deliberately not here
No face matching, on this page or anywhere on a K-12 surface of ours — not on a visitor, not on a student, not on an image attached to a report. No outside-model analysis of a child’s report. The staff console has no safeguarding navigation group: the pages exist and are reachable, and a named menu group is not something this page will claim exists when it does not. And no adoption figures, school counts, testimonials or partner logos anywhere on this site, because we do not have honest ones to publish and an invented one on a child-safety page is the worst possible place to start.
The image takedown channel, and the feature we decided not to build
A platform that hosts uploaded images of minors carries a federal notice-and-removal duty: provide a clear process for a person depicted in an intimate image shared without their consent to request removal, and remove it — along with reasonable efforts at identical copies — inside forty-eight hours of a valid request. Our answer is a public channel with no sign-in, because the duty runs to any person depicted rather than to account holders. It is metered by a token bucket and bot-gated, and the bot gate is honest-off without a secret rather than blocking a real victim out.
The two halves fail in opposite directions on purpose. Intake fails open: a request must be hard to lose, so it is accepted, stored with its deadline, and answered with a ticket reference the requester keeps. Removal fails closed: an unresolved sweep escalates to a human operator inside the deadline rather than quietly closing itself. The request row is trust-and-safety read only — a school or an adviser never reads other people’s takedown requests — and the acknowledgement echoes only the public reference and the deadline, never whether a given image exists on the platform.
The part that is worth publishing is the feature we refused. A self-service lookup, where a requester could type their ticket reference and see their case, was designed and then declined, and the reasoning is short: the row holds the requester-supplied pointer to where the imagery appears, so returning it to whoever holds the reference would hand an abuser the location of the image; a removed-copy count is an existence oracle through the back door; and the image-matching fields are a matching oracle for the image itself. Intersect what would be genuinely new to the victim with what is safe if an abuser reads it and the set is close to empty — the requester already holds the timestamp and the deadline from the acknowledgement. So there is no self-service lookup, and the reference is worth keeping only to quote to a human operator. A response that is safe was not worth building; a response worth building was not safe.
Putting it on a wall
The link on a poster should carry your school. A report sent from it is stored under your school whenever the intake check passes, and who can open it follows the three states described above. The plain address with nothing after it also files, but the report is saved in the Stanley Studios triage queue, and it does not appear on your school’s own triage list: nothing in that queue puts it there. A link carrying your school does not have that gap. A poster on your wall should carry your school.
Per-school link: https://app.homeroom.software/report-a-concern/<your-school-link>
That is the address to put behind a QR code. The school part is your public school link — the same one that already appears in your school’s own web address on this platform — never an internal record id, so the printed address exposes nothing.
A printable poster page: this site serves one for your school link at https://tutus.network/poster/<your-school-link>. It is a single printable page carrying a QR code that points at the per-school link above, that address written out under the code, and the emergency line telling anyone in danger to call 911 first. You print it; nothing is printed or posted for you. The poster page checks only the shape of the school part of the address. It does not check that your school exists on the platform, or which of the three states your school is in, so run the two-step check above before you print.
Before you print: run the two-step check in the module section. A receipt alone tells you only that the intake check passed; your district’s safety administrator opening the school’s safety list and finding the test report is what tells you a report on this link can be opened. It takes a few minutes and it is the only check that is specific to your school.
What we are not claiming, and why
This is the section a safeguarding vendor is least likely to publish, which is exactly why it is here rather than in a footnote. Each item is open, named, and not dressed up.
How long a report is kept, and whether it is encrypted at rest
A report body is stored as plain text with no expiry, and there is no managed-key encryption at rest for it. That is the current state, and it is the single biggest reason this page describes the reporting line in narrow, checkable terms rather than a broad one. A binding retention period is one of the two items marked pending counsel below — not because it is hard to write, but because writing one we do not enforce would be worse than saying this.
What the surrounding infrastructure might hold
The four request-plumbing layers inside the application are closed and checked. What a proxy, a content network, a log shipper or a disk might incidentally retain is not something we have audited end to end, and it is not something we are going to assert from the application side. It is named here as open rather than left for someone else to discover.
There is no way to check back on a report
No follow-up route exists on this platform. The page that would consume one has no link pointing at it from anywhere in the product, and the credential such a page would need could only have been minted by a route that does not exist. The id shown after a successful report is a receipt. On a link or poster code that carries the school, it is a receipt that the report was saved under that school; on the plain link, that it was saved in the Stanley Studios triage queue, which is not a school’s triage list. It is not a credential and it opens nothing.
One word we will not use
This page never uses the broad privacy word that would normally head a section like this, on either surface. The route-level facts are verified and stated above; the storage-level questions in the first two items are open. A word that covered both would be covering those too. A build check in this repository enforces that refusal against this page’s own rendered text, so it cannot quietly come back in a copy edit.
Student data and consent, said plainly
The reporting line has no reporter-identity field by absence rather than by policy: the shape it sends has no such field, so there is structurally nothing to redact or leak about who sent it. Policies change; a field that does not exist does not.
On the reading side, a student named in a report is protected by the same consent chokepoint the rest of the platform enforces, at the route rather than in a screen filter a client request could bypass: a suppressed student’s identifying fields are stripped while the row still counts, so a redaction cannot silently change a total. Access is scoped to one school by the same wall the student record uses. A district-held report’s subject identity is never loaded at all, rather than loaded and then withheld at the last moment.
Nothing produced by this surface is used for advertising, sold, or handed to a data broker. There is no face matching here or anywhere on a K-12 surface of ours, no photo claim, and no outside-model analysis of a child’s report — and none of the platform’s other postures on those subjects are being implied by their absence from this page.
How the money works, honestly
This is honest-off money: no pricing table, no checkout, no live charge, and no payment processor wired to any surface on this site — nothing here can complete a purchase, by design rather than by omission. Nothing on this site sets a price for a school. We are a for-profit vendor and this page is not going to pretend to be a charity; it is also not going to invent a price it cannot take.
There is one place where money and safeguarding touch, and it is worth being exact about it because the answer decides how much of the rest of this page you can lean on. The reporting line is not metered, priced or seat-counted, and it is not a paid add-on. It is gated by the safety module, through the two checks described above.
And then a distinction we are going to state precisely rather than round up in our own favour. Two capabilities on this platform — the crisis-command view and the mass-notification channel — are bound as always-on at the plan layer, which means they cannot be darkened by a downgrade, an unpaid invoice or a deny flip, on the reasoning that a mandated life-safety channel is the wrong thing to hold as leverage. The safety module that gates this reporting line is deliberately NOT in that bound set. It is a broad records module wider than the mandated channel, so it was left out of the always-on carve-out on purpose. The honest summary is therefore: not a paid extra, included in three of the four base plans as the platform ships, and not structurally undarkenable either. Rounding that up to a guarantee would be exactly the kind of sentence this page exists to avoid.
One consequence follows and we would rather say it than let you infer it: because there is no live charge anywhere on this site, there is nothing here that can lapse today and quietly take a safeguarding surface down with it. If we ever wire money to any of this, the honest description of what a lapse does belongs on this page before the switch is thrown, not after.
Pending counsel
Two things are deliberately unwritten on this page, because they would be binding legal claims and we are not going to fabricate them. Both are marked PENDING COUNSEL and land as their own reviewed sections once cleared, and not before.
Mandatory-reporting statute language. Who must report, what triggers the duty, how fast, and to whom differ by state, and in several states they differ by role within the same building. A vendor page that summarised all of that into one confident paragraph would be handing a safeguarding lead a version of the law that is wrong for their jurisdiction, in the one place they are least able to check it. So this page describes what the software does with a report and leaves the duty to the people qualified to state it. If your counsel wants the mechanics in order to write your own policy, the routing, the lanes, the category labels and the audit behaviour are all described above in enough detail to map against.
A binding retention period. Report bodies are held as plain text with no expiry today, which is named in the open-questions section above rather than hidden here. Publishing a retention commitment before the deletion path exists to enforce it would be a promise the storage layer cannot keep, and a retention promise a system does not honour is worse than none: a school would answer a records request on the strength of it.
One statute this page does describe concretely is the federal notice-and-removal duty behind the image takedown channel, and it is described as what our code does — a public intake, a stored deadline, a ticket reference, an escalation to a human inside the window — not as legal advice to a school about its own obligations.
Common questions
These are the questions a safeguarding lead actually asks, answered at the level a safeguarding lead actually needs.
Can a student file a report right now, without signing in?
Yes, on a link that carries their school, wherever the intake check passes. The reporting page at /report-a-concern/<school> sends the report to a route that has no sign-in requirement in it at all: no session check, no bearer token, no staff role. The submit call sets a hard override that drops the Authorization header even for a reporter who happens to also be signed in to the school platform in the same browser, so a signed-in student’s session cannot ride along and identify them. Where the intake check passes, the server answers 201 with a receipt id, and the page shows that id back. Where it fails, the server answers the uniform not-found and stores nothing, and the page says so after the send. Whether anyone can then open a stored report depends on the three states described in the module section. A link with no school on the end files too, by a different route — see the next answer.
What happens on the plain /report-a-concern link with no school on the end?
The plain link files: the report is saved in the Stanley Studios triage queue. That page carries its own form with the same two things to fill in — a concern category from the list of six and the report text — and no field for who is reporting. It posts to a second intake route, separate from the school-scoped one, with no sign-in requirement and no school module check in it, and the call drops the Authorization header the same way, so a signed-in session cannot ride along. The server stores the report in that queue and answers 201 with two keys, a receipt id and a triage state, and the page shows the id back. The id is a receipt that the report was saved in the Stanley Studios triage queue, not that it reached a school: nothing that happens to a report in that queue places it on a school’s own triage list. The id promises no response and no turnaround. If the intake cannot be reached, or the answer carries no receipt, the page says so in words, keeps what was typed in the box, and shows no receipt it did not get. The same page also takes a poster code, and a code that resolves files the report under that school, exactly as that school’s own link does; who can then open it depends on the three states described in the module section. A report filed under a school is the only kind a school’s own safety list can show, which is why a link carrying the school is the one to put on a wall.
Exactly which fields does a report carry?
It depends on the link. On a link that carries the school, three, and the shape cannot grow a fourth by accident: a concern category from a closed list of six, the report text, and the public school link the reporter arrived on. The input type the form fills in has no reporter field to fill, so there is nothing to leave blank. On the server the body schema is strict, which means an unexpected key is a hard rejection rather than a silent drop — a stray contact field cannot be quietly accepted and stored. Before the schema runs, the route checks the field names against a list of reporter-field spellings (a reporter id, name, email, phone, IP address or user id, a submitter id, and a bare email, IP address or user id) and refuses the report with nothing stored if one is present. It reads the names of fields, never the words of a report. Three layers, and the first one is that the field does not exist. The plain link with no school sends two fields: the category and the report text. Its body schema is strict in the same way and accepts one more optional field, a source host, which the reporting page does not send. The same reporter-field check runs first on that route before its own insert, so the same three layers hold there, and the first is still that the field does not exist.
Your form does not ask who I am. Does the rest of the system quietly capture it anyway?
That is the right question, and it is the one that produced the most work here. A route can be clean and still sit under request plumbing that is not. Three always-on layers were found capturing the reporter anyway: the web framework’s built-in request logger, which is a separate logging instance from the platform’s redacting one and writes the caller’s address on every request; the general rate limiter, which writes a bucket key containing the raw address into shared storage, timestamp-correlatable with the stored report; and the observability layer, which binds a signed-in user id onto the access-log line. The first is closed at the route by dropping its log level so the request and response lines are never written. The second and third are closed by a single registry of shielded intake routes that both layers consult. A fourth layer — a route’s own token bucket — is closed at the route with a salted key, never a raw address. Four layers, four separate closures, and none of them is the same fix.
How do you know the shield did not quietly come off?
Because four separate checks run in the build and each one is keyed on a different population, which is the only reason they can see each other’s blind spots. check:anon-intake-registry fails the build when a route’s own comments claim membership of the shielded registry that it does not actually have — a claim in a comment is not a control, and that exact gap once left two public routes exposed for weeks while the file asserted they were closed. check:anon-intake-kv-shape opens the files that own a registered route and asserts their storage keys are salted, not raw. check:anon-intake-orphan asks the inverse question that neither of the other two can: does a route shield itself at the log layer and then lack the registry membership that closes the others? Run bare, it exited non-zero on four such orphans. check:anon-intake-client-noauth holds the browser side of the wall. A gate whose population is the registry itself is blind by construction to a route missing from the registry; that is why the third one exists.
Why do you not use one broader privacy word for all of this?
Because the route-level facts and the storage-level facts are different questions, and only the first set is settled. What is true and verified: there is no reporter field on the wire, none on the stored row, no sign-in on the submit path, no bearer token sent, no raw address in the rate-limit key, no user id on the access line, and no request or response log line for the call. What is still open: how long a report body is retained — today it is stored as plain text with no expiry — whether it is encrypted at rest under a managed key, and what a proxy, a CDN, a log shipper or a disk might incidentally hold. Three open items, named. A word that covers all of it would be covering those three too, so this page states the narrower thing it can stand behind and stops there. The claim register in this repository still carries that family as gated for exactly these three reasons, and a build check enforces it against this page’s own text.
Can I check back on a report I filed?
No. There is no follow-up route on this platform — the check-back path the reporting page was built against does not exist on the server, and the page that would consume it has no link pointing at it from anywhere in the product. The id shown after a successful report is a RECEIPT, and what it proves depends on the link. On a link or poster code that carries the school, it proves the report was saved under that school, and nothing about who can open it. On the plain link, it proves the report was saved in the Stanley Studios triage queue, and nothing more: it does not reach a school’s own triage list. Either way it is not a credential that opens anything. It is worth keeping only as a record that the report was stored. Saying otherwise would be inventing a door.
Can the link be used to find out which schools are on your platform?
No, and the design is specific about it. A school link that does not resolve, a school link whose intake check fails, and a tampered or expired poster code all take the same uniform 404 with the same message and the same body, so they are not distinguishable from outside. The report’s optional subject student is treated the same way: an unknown student id and a student who belongs to a different school converge on the same result — the structured reference is dropped and the report is still filed, with the narrative untouched. A status code that varied with a submitted id would be an enumeration oracle on an open route, so it does not vary. On a school link the success body carries the same three keys whatever was submitted: the report id, its triage state and the lane it went to. A fourth key appears only when the report’s audit entry fails to write, which varies with our infrastructure and never with what was sent. The plain link’s success body is two keys: the report id and its triage state.
What if a student reports a member of staff?
The narrative is stored exactly as written — a student’s account of a named adult is never edited or dropped. Like every school-link report, it is filed under the school, and who can open it depends on the three states described in the module section. What is dropped is the structured staff id field, on this open route only. That field is the key to a recusal wall with four enforcement points, so a stranger who guessed a serving administrator’s id could otherwise have filed a fabricated report naming them and walled that administrator off from ever seeing it. Rejecting the whole report would have been the other harm: it would deprive a child of filing. So the report is filed, the words are untouched, and only the id is dropped: the answer still names the staff-misconduct lane, the stored report carries no staff id, and it sits among the school’s ordinary reports with nothing for the recusal wall to act on. The web reporting form never sends that field at all. The recusal wall itself is untouched and still fail-closed — what was removed is a stranger’s ability to arm it.
What happens if your audit ledger write fails after the report is stored?
The reporter is told the truth, which is that the report was filed. The report row commits first, stored under the school; the hash-chained ledger entry is appended afterwards, outside that transaction. If that append throws, the old behaviour was a 500, and the reporting page then rendered every non-honest-off 500 as Nothing was recorded — a confident falsehood about the one fact the reporter needs, told to someone who will then stop escalating. So the append failure is caught and the 201 still goes back, because the 201 is true. The alarm gets louder rather than quieter: an error-level log line fires and names the specific report whose ledger entry is missing, and the response itself carries a flag saying the audit entry was not recorded, so the gap is visible to the caller and to the tests, not only in a log nobody reads. Rolling the report back to keep the ledger tidy would have destroyed a child’s report to protect a bookkeeping invariant. The reporting page changed too: today, on a school link, it renders a 500 as a page that says it cannot tell whether the report was saved, and it keeps what was typed.
Does your audit ledger prove nothing was tampered with?
It proves no entry that IS there was altered, and it does not prove that none is missing. The ledger is append-only and each entry is chained by hash to the school’s previous one, so a replay detects an edit or a removal from the middle of the chain. But an omission at the tip — a safety row committed while its ledger append failed — leaves the chain perfectly intact, because there is no hole to find. The verify endpoint therefore returns a completeness verdict of null, meaning NOT MEASURED, rather than true: establishing completeness needs an expectation derived from outside the ledger, and no such per-school read exists. A consumer checking that verdict now alarms instead of passing. Narrowing it back to a bare pass to quiet that alarm is the one repair we will not make.
Is this an emergency line? Does someone watch it around the clock?
No, and the reporting page says so above the form, before anything else: in an emergency, call your local emergency number first. Nothing here claims that anyone monitors the line, and nothing here dispatches. The related panic-alert surface is honest in the same direction — a panic alert is RECORDED, mapped and given a lifecycle, and it is not delivered to a responder by this software. The planner that builds the alert is a pure function with no outbound call underneath it, the public-safety integration is unset, so the external verdict comes back as unconfigured, and with no mail provider key each responder message is planned as queued, never marked sent. A lifecycle stamp that would have written “the alert went out” was removed, because it would have made that claim the day a key was provisioned, silently. The panic device and the emergency routing are a hardware partner’s, not ours.
Our district wants to hold reports before a school sees them. Is that possible?
Yes, where both checks described in the module section pass, and there it is the default for a walk-up report. A report arrives with no school grant recorded, so district policy holds it until a district actor releases it. What a held report looks like to a school matters as much as the hold itself: where both checks pass, the row STAYS in the school’s list, stripped rather than hidden, with a flag marking it held. Hiding it would hide that a report about this school exists, and a school that cannot see a report exists cannot ask for it to be released — the existence is the actionable part; the content is what is withheld. On the single-report read the same choice is made deliberately in the opposite direction from a refusal: a 403 there would be mapped by the console to a sentence telling a legitimate administrator they have no access to triage at all, which would be false, and they would stop looking. A held report’s subject identity is never even loaded. Where the intake check passes and the read check fails, none of this is reachable: the list, the single-report read and the release all refuse, for the district as well as the school.
What does a member of staff see, and what do they have to sign in for?
Everything on the reading side needs a sign-in, and that is on purpose: a staff read of a report about a child is exactly the event an access log should hold. The list and the single-report read both require the safety module, through the read check described in the module section, and a records-staff role; both are are scoped to one school, and route every identity field through the consent chokepoint the rest of the platform uses — a suppressed student’s name is masked, and the row still counts. A report marked heightened-confidentiality is redacted on the LIST for every role; only the single-record read can reveal it. Reports default to heightened-confidentiality, not the other way round. The submit side requires none of this, which is the whole design: the person reporting is not the person being audited.
We already have an online form. What does this do that a form does not?
A generic form collects a submission. The difference is everything that happens on either side of the collection: an input type with no reporter field so there is nothing to redact; a strict schema that rejects an unexpected key rather than storing it; a session strip so a signed-in reporter’s own bearer token is left off the submission entirely; four request-plumbing layers closed separately and four build checks that fail the build when one comes off; a uniform not-found so the link cannot be used to enumerate schools; a lane label from the category (threat assessment, image abuse or general triage) named on the receipt; a recusal wall; a district custody hold that, where both checks pass, keeps the row on the school’s list while withholding the content; a hash-chained audit entry; and a failure path that will not tell a child nothing was recorded once the report row is stored. A form gets you the first hour of that.
Does this cost anything?
Money is honest-off on this page: there is no pricing table, no checkout, and no payment processor wired to any surface on this site — nothing here can complete a purchase. Nothing on this site sets a price for a school.
Is any legal or mandatory-reporting language on this page binding?
No. Jurisdiction-specific mandatory-reporting statute language and a binding retention period are marked PENDING COUNSEL below and are deliberately unwritten rather than fabricated. Mandatory-reporting duties differ by state and a wrong version is worse than none. The one statute this page does describe concretely — the federal notice-and-removal duty behind the image takedown channel — is described as what the code does, not as legal advice.
What this page is, and is not, claiming
Two reporting routes, and neither has a sign-in on it: a reporter on a link carrying their school can file today wherever the intake check passes, and whether anyone can then open the report depends on a second check, described in the module section. The plain link with no school also files: it carries its own form and the report is saved in the Stanley Studios triage queue, which is not any school’s triage list, and if the page gets no clear answer from that queue, it says after the send that it cannot tell whether the report was saved, and shows no receipt it did not get. The reading side does require a sign-in, is scoped to one school, is consent-gated on the way out, and carries a recusal wall that is fail-closed. There is no follow-up route and the id a reporter is shown is a receipt, not a credential. A panic alert is recorded and is not delivered; there is no public-safety integration and no dispatcher underneath it. This site serves a printable poster page for a school link; the printing is yours. Three storage questions — retention, encryption at rest, and what surrounding infrastructure holds — are open and named rather than covered by a bigger word, and this page uses no such word about either surface. The wider safeguarding console is shipped engineering. Money is honest-off: no pricing table, no checkout, no processor wired anywhere on this site. Two items are marked pending counsel rather than invented. No adoption figures, testimonials or partners appear here, and no competitor is named.
Earlier revisions of this page told schools that the reporting route was missing, that the live line required a sign-in, and that the plain school-less link could not file. All three were false, all three discouraged reporting, and each was corrected here after measuring the client call, the route registration, the schema and the module default at their source — the school-less link now carries its own form and files to the Stanley Studios triage queue. A correction is a claim and gets the same audit as the thing it corrects.