Input Controls
Student Privacy
student-privacy finds student-record content in a request, such as a student ID or the word transcript. It replaces student ID phrases before the model sees them, or blocks the request. With the age gate on, it also blocks student records about a child under 13.
A redact verdict does not mean that all student information is gone. Only student ID phrases are replaced: a word such as transcript sets the verdict but stays in the text. The age check reads the message text only. It does not verify identity or consent.
Configuration
pack:
name: student-privacy-example-1
version: "1.0.0"
enabled: true
policies:
chain:
- student-privacy
policy:
student-privacy:
action: redact
age_gate: trueVerify the age gate with a pack test
Add these cases to the file and run verdictan policy test --json:
testing:
suites:
- name: student-privacy
cases:
- name: blocks-under-13-student-record
input: {messages: [{role: user, content: "Student ID: AB-1234 and age 12."}]}
expected: {verdict: block, reason_code: student_privacy.age_gate}
- name: redacts-student-id-age-13
input: {messages: [{role: user, content: "Student ID: AB-1234 and age 13."}]}
expected: {verdict: redact, reason_code: redact.applied}
- name: redacts-transcript-only
input: {messages: [{role: user, content: "Send the transcript."}]}
expected: {verdict: redact, reason_code: redact.applied}
- name: allows-clean-education
input: {messages: [{role: user, content: "Help an age 12 learner with fractions."}]}
expected: {verdict: allow, reason_code: ok}All four pass. The last case shows that an age alone is not a match. With action: block, the marker cases are blocked with student_privacy.block instead.
The test runner checks the request only. Test response redaction, response blocks, and streaming through a gateway.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
action | redact or block | redact | What happens to a match in the request. redact also redacts the response. block also turns on the response block check. |
age_gate | boolean | false | A match together with an age from 1 to 12 in the text blocks the request |
How it works
- The gateway joins the request messages.
- It looks for the built-in terms at word boundaries and for the student ID phrase. See Input detection.
- With
age_gate, it reads the first age expression and notes whether the age is from 1 to 12. - No term or ID phrase:
allowwithstudent_privacy.clean. An age alone does not match. - A match with an age from 1 to 12:
blockwithstudent_privacy.age_gate. - A match with
action: block:blockwithstudent_privacy.block. - A match with
action: redact:redactwithstudent_privacy.redact. The redaction pass then replaces student ID phrases before dispatch. - The details record
action,age_gate,under_13,student_id_like, and the firstkeyword_hit. - When the provider responds, the gateway applies the output behavior of
action. See Output behavior.
Input detection
| Detector | Matches |
|---|---|
| Built-in terms | student id, student_id, transcript, iep, 504 plan, grade, gpa, disciplinary, ferpa |
| ID phrase | student id, student identifier, or student number, an optional :, #, or -, then at least four letters, digits, or hyphens |
| Inline age | age N or N years old, with a one- or two-digit N |
Case does not matter.
| Text | With age_gate: false | With age_gate: true |
|---|---|---|
Help an age 12 learner with fractions. | allow | allow |
Student ID: AB-1234 | redact | redact |
Student ID: AB-1234, age 12 | redact | block with student_privacy.age_gate |
Send the transcript. | redact | redact |
The age gate reads message text only. It does not use account profiles, headers, verified birth dates, or consent records, and it has no separate level for ages 13 to 17.
Output behavior
With gateway 0.1.26 or later, the output behavior applies to streamed and buffered responses on Chat Completions, Responses, and Messages.
| Configuration | Response |
|---|---|
action: redact | The redaction pass replaces student ID phrases in the supported text fields. A changed response has a nested student_privacy.redact result and the top-level reason redact.applied. |
action: block | A response that contains student id, studentid, student identifier, student number, studentnumber, transcript, or ferpa is blocked with student_privacy.block. student numbers does not match. |
The nested redaction result names student-privacy only when the chain has no pii-detector, hipaa-phi-detector, or dlp-filter. See Output redaction reason codes.
A buffered response block returns HTTP 400 without the provider body. A streamed response block ends the stream with the error event of the route:
| Route | Error event |
|---|---|
/v1/chat/completions | A data: event with the code content_policy_violation, then data: [DONE] |
/v1/responses | A final event: error frame |
/v1/messages | An Anthropic event: error frame |
In a stream, the gateway holds back text that can be the start of a blocked phrase. Thus the client does not get the phrase or the identifier after it. The request records the usage that the provider reports. See Output blocks in a stream.
Phase and verdicts
| Item | Value |
|---|---|
| Input phase | The joined request messages |
| Output phase | Redacts ID phrases with redact, or blocks selected terms with block |
| Verdicts | allow, redact, block |
| No match | student_privacy.clean |
| Redaction | student_privacy.redact |
| Block | student_privacy.block |
| Age gate block | student_privacy.age_gate |
Limits and failure behavior
- This is deterministic text matching. Legal, consent, identity, and retention controls need their own processes.
- Only the student ID phrase is replaced. A term such as
transcriptgivesredactwith nothing to replace, and the text goes out unchanged. Check the forwarded payload. - The policy result does not list redaction targets. The redaction pass reports the replacements.
- The response block check uses fewer terms than the request check: not
iep,504 plan,grade,gpa, ordisciplinary. - The response block check runs only with
block, and response redaction only withredact. - Request blocks happen before the provider call on every transport.
- A stream that ends with
student numberorstudentidis not blocked. Only that phrase gets to the client, with no identifier after it. - With two or more spaces between
studentandid, the textstudentcan get to the client before a stream block. gradeandgpaappear in ordinary education text. Review false matches before rollout.- Combine with
pii-detectorordlp-filterfor other personal or institutional data.
| Problem | Cause | Action |
|---|---|---|
| A request with age 12 was allowed | No term or ID phrase matched, age_gate is off, or an earlier age expression set the age. | Include a marker in the test and check the first age expression. |
redact but the text did not change | Only ID phrases are replaced. | Use block, or add a dlp-filter pattern for the term. |
A response with grade was not blocked | The response check uses fewer terms. | Test with the documented output terms, or use another output control. |
| The control is not in the results | It is not in the effective chain, or a condition or target excludes it. | Check the applied route chain and targeting. |
Next steps
- PII Detector: broader personal identifiers.
- DLP Filter: your own terms and patterns.
- Test Configuration: a regression suite from real examples.